lightningcss 0.2.0-arm-linux-gnu → 1.33.0.1-arm-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 +119 -0
- data/ext/lightningcss/include/lightningcss.h +2 -0
- data/ext/lightningcss/lightningcss.c +7 -0
- 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/node.rb +220 -0
- data/lib/lightningcss/options.rb +5 -0
- data/lib/lightningcss/parse_result.rb +52 -0
- data/lib/lightningcss/transformer.rb +5 -0
- data/lib/lightningcss/version.rb +1 -1
- data/lib/lightningcss/visitor.rb +37 -0
- data/lib/lightningcss.rb +16 -0
- data/rust/Cargo.lock +63 -30
- data/rust/Cargo.toml +2 -3
- data/rust/build.rs +22 -0
- data/rust/src/lib.rs +38 -3
- data/rust/src/options.rs +6 -0
- data/sig/lightningcss/backend.rbs +3 -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/transformer.rbs +3 -0
- data/sig/lightningcss/visitor.rbs +24 -0
- data/sig/lightningcss.rbs +8 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f996660960f0f35d06985936300415a8ce668ab5e8bc127662b0ea4d23196ea3
|
|
4
|
+
data.tar.gz: e0b2dc6dfa403e093bbb3da2308cdc422032e72e620a70c59cc625fa32e9afdc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c45fd06eff33a4ab77982392a9a1399f7103018a3039cea43306ba3876820b9bf7282d1d9988dbdf7ed809f43eef95d2b3f008e44b1ae33dfdb739a4a1a8a5bf
|
|
7
|
+
data.tar.gz: 6a5dbf61055622e5f5864905bfc36911a9db0f54a1e2e90cd94b3174a8ababc828965d409c1ff8b067067267b6d28eb8460a79238bc425bb3a2338ff36039e7b
|
data/README.md
CHANGED
|
@@ -27,6 +27,16 @@ bundle add lightningcss
|
|
|
27
27
|
|
|
28
28
|
Precompiled gems are published for Linux (gnu and musl) and macOS, on x86_64, aarch64, and arm. On those platforms nothing is compiled at install time. Anywhere else the gem builds from source and needs the [Rust toolchain](https://rustup.rs).
|
|
29
29
|
|
|
30
|
+
### Versioning
|
|
31
|
+
|
|
32
|
+
The gem carries the version of the Lightning CSS release it packages. Gem v1.33.0 bundles [Lightning CSS v1.33.0](https://github.com/parcel-bundler/lightningcss/releases/tag/v1.33.0).
|
|
33
|
+
|
|
34
|
+
Lightning CSS publishes its Rust crate under a different number than it releases under: v1.33.0 ships the crate as `1.0.0-alpha.72`. The gem follows the release, since that is the version everything else calls it. `LightningCSS.lightningcss_version` reports the crate version a given build was compiled against.
|
|
35
|
+
|
|
36
|
+
So the version tracks Lightning CSS, not the Ruby API here. A minor bump is a minor Lightning CSS release, and says nothing about whether this gem's own API moved. Pin on the Lightning CSS version you want.
|
|
37
|
+
|
|
38
|
+
If the gem needs releasing again for the same Lightning CSS version, that release adds a fourth digit, as in v1.33.0.1.
|
|
39
|
+
|
|
30
40
|
### Usage
|
|
31
41
|
|
|
32
42
|
#### Transforming
|
|
@@ -131,6 +141,113 @@ LightningCSS.transform_style_attribute("color: #ff0000; border: none", minify: t
|
|
|
131
141
|
|
|
132
142
|
Having no selectors and no names, it takes neither `scope` nor `css_modules`, and says so when given one.
|
|
133
143
|
|
|
144
|
+
#### Parsing
|
|
145
|
+
|
|
146
|
+
`parse` answers what Lightning CSS read a stylesheet as, without printing anything. It takes `filename` and `error_recovery`, the two options that change how a stylesheet is read.
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
parsed = LightningCSS.parse(".card .title:hover { color: #f00 }")
|
|
150
|
+
|
|
151
|
+
parsed.stylesheet #=> the AST exactly as Lightning CSS serializes it
|
|
152
|
+
parsed.warnings #=> []
|
|
153
|
+
parsed.source #=> ".card .title:hover { color: #f00 }"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The AST is Lightning CSS's own, so its shapes are the ones its [JavaScript visitors](https://lightningcss.dev/transforms.html) see and [`lightningcss/node/ast.d.ts`](https://github.com/parcel-bundler/lightningcss/blob/master/node/ast.d.ts) describes. It is what Lightning CSS understood, not what was written: `#f00` reads as an `rgb` with `r` of `255.0`, and a `0` margin reads as `0px`.
|
|
157
|
+
|
|
158
|
+
#### Walking the AST
|
|
159
|
+
|
|
160
|
+
`root` answers the stylesheet as a `LightningCSS::Node`, which walks, reads its fields by name, and knows what it sits inside.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
root = LightningCSS.parse(source).root
|
|
164
|
+
|
|
165
|
+
root.type #=> "stylesheet"
|
|
166
|
+
root.keys #=> the fields this node carries
|
|
167
|
+
root.fields #=> those fields and their values, without its type and location
|
|
168
|
+
root.child_nodes #=> the nodes directly under it
|
|
169
|
+
root.every("declaration") #=> every declaration in the stylesheet
|
|
170
|
+
root.each #=> an Enumerator over every node
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Every object in the tree is a node, so reads chain all the way down.
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
rule = root.rules.first
|
|
177
|
+
|
|
178
|
+
rule.type #=> "style"
|
|
179
|
+
rule.value.selectors.first.map(&:type) #=> ["class", "combinator", "class", "pseudo-class"]
|
|
180
|
+
rule.value.declarations.declarations.first.value.r
|
|
181
|
+
#=> 255.0
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Most objects say what they are with their own `type`. The stylesheet, a declaration block, and a declaration do not, so a node names those `"stylesheet"`, `"declaration-block"`, and `"declaration"` from where it sits. Anything else without a `type`, like the `value` a rule wraps, answers `nil`.
|
|
185
|
+
|
|
186
|
+
Inspecting a node shows every field it carries.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
#<LightningCSS::Node style value=#<LightningCSS::Node>>
|
|
190
|
+
#<LightningCSS::Node location=0:1 selectors=[... 1 item] declarations=#<LightningCSS::Node declaration-block> rules=[]>
|
|
191
|
+
#<LightningCSS::Node declaration property="color" value=#<LightningCSS::Node rgb>>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Lightning CSS names its fields in camelCase, and a field answers to its snake_case name too.
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
block.important_declarations # the same field as block.importantDeclarations
|
|
198
|
+
query.media_queries # mediaQueries
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Lightning CSS records where each rule starts and nothing else, so `location` is a rule's line, counted from 0, and its column, counted from 1 in UTF-16 code units. There are no end positions, so a node has no `slice` and there is no `at(offset)`.
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
rule.value.location
|
|
205
|
+
#=> {"source_index" => 0, "line" => 0, "column" => 1}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`ancestors` answers what a node sits inside, and `rule?` says whether it is a rule.
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
declaration = root.every("declaration").first
|
|
212
|
+
declaration.ancestors.find(&:rule?).type
|
|
213
|
+
#=> "style"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Nodes pattern match, and nest, since a field holding an object comes back as a node. Patterns take snake_case names, and a node's `type` matches even when Lightning CSS gave it none.
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
declaration => { type: "declaration", property:, value: { type: "rgb", r: } }
|
|
220
|
+
property #=> "color"
|
|
221
|
+
r #=> 255.0
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
`to_h` and `to_json` answer the AST the node wraps.
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
declaration.value.to_h
|
|
228
|
+
#=> {"type" => "rgb", "r" => 255.0, "g" => 0.0, "b" => 0.0, "alpha" => 1.0}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`LightningCSS::Visitor` answers a node with the method named after its type, and walks through anything nothing answers.
|
|
232
|
+
|
|
233
|
+
```ruby
|
|
234
|
+
class Colors < LightningCSS::Visitor
|
|
235
|
+
def visit_style_rule(node)
|
|
236
|
+
puts node.value.location["line"]
|
|
237
|
+
|
|
238
|
+
visit_children(node)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def visit_declaration(node)
|
|
242
|
+
puts node.property
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
Colors.new.visit(LightningCSS.parse(source))
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
A rule is answered as one, as in `visit_style_rule`, `visit_media_rule`, or `visit_namespace_rule`. Lightning CSS calls the rule `style`, but `namespace` and `nesting` are also parts of a selector, so `visit_namespace` answers `svg|rect` and `visit_namespace_rule` answers `@namespace`. Hyphens become underscores, so a `pseudo-class` is answered by `visit_pseudo_class`.
|
|
250
|
+
|
|
134
251
|
#### Reusing options
|
|
135
252
|
|
|
136
253
|
`LightningCSS::Transformer` holds a set of options to use across many stylesheets. Options given to a call are merged over the ones it was built with.
|
|
@@ -145,6 +262,8 @@ transformer.with(scope: "[s]")
|
|
|
145
262
|
|
|
146
263
|
It answers `call` as well, so it can be handed to anything expecting something callable.
|
|
147
264
|
|
|
265
|
+
`transformer.parse` reads with the `filename` and `error_recovery` it holds and leaves the rest, which only matter to printing.
|
|
266
|
+
|
|
148
267
|
### Options
|
|
149
268
|
|
|
150
269
|
| Option | Type | Description |
|
|
@@ -23,6 +23,8 @@ typedef struct LightningCssResult {
|
|
|
23
23
|
|
|
24
24
|
struct LightningCssResult lightningcss_transform(const char *code, const char *options_json);
|
|
25
25
|
|
|
26
|
+
struct LightningCssResult lightningcss_parse(const char *code, const char *options_json);
|
|
27
|
+
|
|
26
28
|
struct LightningCssResult lightningcss_transform_style_attribute(const char *code,
|
|
27
29
|
const char *options_json);
|
|
28
30
|
|
|
@@ -90,6 +90,12 @@ static VALUE rb_transform(VALUE self, VALUE code, VALUE options) {
|
|
|
90
90
|
return call(lightningcss_transform, code, options);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
static VALUE rb_parse(VALUE self, VALUE code, VALUE options) {
|
|
94
|
+
(void) self;
|
|
95
|
+
|
|
96
|
+
return call(lightningcss_parse, code, options);
|
|
97
|
+
}
|
|
98
|
+
|
|
93
99
|
static VALUE rb_transform_style_attribute(VALUE self, VALUE code, VALUE options) {
|
|
94
100
|
(void) self;
|
|
95
101
|
|
|
@@ -123,6 +129,7 @@ void Init_lightningcss(void) {
|
|
|
123
129
|
rb_eOptionError = rb_define_class_under(rb_mLightningCSS, "OptionError", rb_eError);
|
|
124
130
|
rb_eBundleError = rb_define_class_under(rb_mLightningCSS, "BundleError", rb_eError);
|
|
125
131
|
|
|
132
|
+
rb_define_singleton_method(rb_mBackend, "parse", rb_parse, 2);
|
|
126
133
|
rb_define_singleton_method(rb_mBackend, "transform", rb_transform, 2);
|
|
127
134
|
rb_define_singleton_method(rb_mBackend, "transform_style_attribute", rb_transform_style_attribute, 2);
|
|
128
135
|
rb_define_singleton_method(rb_mBackend, "bundle", rb_bundle, 2);
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/lightningcss/backend.rb
CHANGED
|
@@ -8,6 +8,11 @@ module LightningCSS
|
|
|
8
8
|
unavailable(__method__)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
#: (String, String) -> String
|
|
12
|
+
def parse(_code, _options_json)
|
|
13
|
+
unavailable(__method__)
|
|
14
|
+
end
|
|
15
|
+
|
|
11
16
|
#: (String, String) -> String
|
|
12
17
|
def transform_style_attribute(_code, _options_json)
|
|
13
18
|
unavailable(__method__)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# One object in the AST Lightning CSS parsed a stylesheet to.
|
|
5
|
+
#
|
|
6
|
+
# root = LightningCSS.parse(".a { color: red }").root
|
|
7
|
+
#
|
|
8
|
+
# root.type #=> "stylesheet"
|
|
9
|
+
# root.every("declaration") #=> every declaration in the stylesheet
|
|
10
|
+
# root.rules.first.value.location #=> {"source_index" => 0, "line" => 0, "column" => 1}
|
|
11
|
+
#
|
|
12
|
+
# Every object in the tree is a node, so reads chain all the way down. Most say what they are
|
|
13
|
+
# with their own `type`. The stylesheet, a declaration block, and a declaration do not, so a node
|
|
14
|
+
# names those from where it sits.
|
|
15
|
+
#
|
|
16
|
+
class Node
|
|
17
|
+
include Enumerable #[LightningCSS::Node]
|
|
18
|
+
|
|
19
|
+
POSITION = ["type", "loc"].freeze #: Array[String]
|
|
20
|
+
DECLARATIONS = ["declarations", "importantDeclarations"].freeze #: Array[String]
|
|
21
|
+
|
|
22
|
+
attr_reader :parent #: LightningCSS::Node?
|
|
23
|
+
attr_reader :field #: String?
|
|
24
|
+
|
|
25
|
+
#: (Hash[String, untyped], ?LightningCSS::Node?, ?String?) -> void
|
|
26
|
+
def initialize(attributes, parent = nil, field = nil)
|
|
27
|
+
@attributes = attributes
|
|
28
|
+
@parent = parent
|
|
29
|
+
@field = field
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#: () -> String?
|
|
33
|
+
def type
|
|
34
|
+
attributes.fetch("type") { inferred_type }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#: () -> String?
|
|
38
|
+
def underscored_type
|
|
39
|
+
return nil unless (name = type)
|
|
40
|
+
|
|
41
|
+
name = name.tr("-", "_").downcase
|
|
42
|
+
|
|
43
|
+
rule? ? "#{name}_rule" : name
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#: () -> bool
|
|
47
|
+
def rule?
|
|
48
|
+
field == "rules" && attributes.key?("type")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#: () -> Hash[String, Integer]?
|
|
52
|
+
def location
|
|
53
|
+
attributes["loc"]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#: (String) -> untyped
|
|
57
|
+
def [](key)
|
|
58
|
+
attributes[key]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#: () -> Array[String]
|
|
62
|
+
def keys
|
|
63
|
+
attributes.keys
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#: () -> Hash[String, untyped]
|
|
67
|
+
def to_h
|
|
68
|
+
attributes
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
#: (?untyped) -> String
|
|
72
|
+
def to_json(state = nil)
|
|
73
|
+
state ? attributes.to_json(state) : attributes.to_json
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#: (Array[Symbol]?) -> Hash[Symbol, untyped]
|
|
77
|
+
def deconstruct_keys(keys)
|
|
78
|
+
found = {} #: Hash[Symbol, untyped]
|
|
79
|
+
|
|
80
|
+
if keys
|
|
81
|
+
keys.each do |name|
|
|
82
|
+
key = field_for(name.to_s)
|
|
83
|
+
|
|
84
|
+
if key
|
|
85
|
+
found[name] = wrap(attributes[key], key)
|
|
86
|
+
elsif name == :type && type
|
|
87
|
+
found[name] = type
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
else
|
|
91
|
+
found[:type] = type if type
|
|
92
|
+
attributes.each_key { |key| found[key.to_sym] = wrap(attributes[key], key) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
found
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#: () -> Array[LightningCSS::Node]
|
|
99
|
+
def child_nodes
|
|
100
|
+
@child_nodes ||= attributes.flat_map { |key, value| key == "loc" ? [] : nodes_in(value, key) }.freeze
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
#: () { (LightningCSS::Node) -> void } -> void
|
|
104
|
+
#: () -> Enumerator[LightningCSS::Node, void]
|
|
105
|
+
def each(&)
|
|
106
|
+
return enum_for(:each) unless block_given?
|
|
107
|
+
|
|
108
|
+
yield self
|
|
109
|
+
|
|
110
|
+
child_nodes.each { |child| child.each(&) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
#: () -> Array[LightningCSS::Node]
|
|
114
|
+
def ancestors
|
|
115
|
+
parent ? [parent, *parent.ancestors] : []
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#: (String) -> Array[LightningCSS::Node]
|
|
119
|
+
def every(type)
|
|
120
|
+
each.select { |node| node.type == type || node.underscored_type == type }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#: () -> Hash[String, untyped]
|
|
124
|
+
def fields
|
|
125
|
+
attributes.except(*POSITION)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
#: () -> String
|
|
129
|
+
def inspect
|
|
130
|
+
described = fields.map { |key, value| described_field(key, value) }
|
|
131
|
+
position = location
|
|
132
|
+
described.unshift("location=#{position["line"]}:#{position["column"]}") if position
|
|
133
|
+
|
|
134
|
+
"#<#{self.class.name}#{" #{type}" if type}#{" #{described.join(" ")}" unless described.empty?}>"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
#: (Symbol, *untyped) -> untyped
|
|
138
|
+
def method_missing(name, *arguments)
|
|
139
|
+
key = field_for(name.to_s)
|
|
140
|
+
|
|
141
|
+
return super unless key
|
|
142
|
+
|
|
143
|
+
wrap(attributes[key], key)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
#: (Symbol, ?bool) -> bool
|
|
147
|
+
def respond_to_missing?(name, include_private = false)
|
|
148
|
+
!field_for(name.to_s).nil? || super
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
protected
|
|
152
|
+
|
|
153
|
+
attr_reader :attributes #: Hash[String, untyped]
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
#: () -> String?
|
|
158
|
+
def inferred_type
|
|
159
|
+
if parent.nil? && attributes.key?("rules") && attributes.key?("sources")
|
|
160
|
+
"stylesheet"
|
|
161
|
+
elsif attributes.key?("importantDeclarations")
|
|
162
|
+
"declaration-block"
|
|
163
|
+
elsif DECLARATIONS.include?(field) && attributes.key?("property")
|
|
164
|
+
"declaration"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
#: (untyped, String) -> Array[LightningCSS::Node]
|
|
169
|
+
def nodes_in(value, key)
|
|
170
|
+
case value
|
|
171
|
+
when Hash
|
|
172
|
+
[Node.new(value, self, key)]
|
|
173
|
+
when Array
|
|
174
|
+
value.flat_map { |item| nodes_in(item, key) }
|
|
175
|
+
else
|
|
176
|
+
[]
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
#: (String, untyped) -> String
|
|
181
|
+
def described_field(key, value)
|
|
182
|
+
case value
|
|
183
|
+
when Array
|
|
184
|
+
"#{key}=#{value.empty? ? "[]" : "[... #{counted(value)}]"}"
|
|
185
|
+
when Hash
|
|
186
|
+
type = Node.new(value, self, key).type
|
|
187
|
+
|
|
188
|
+
"#{key}=#<#{self.class.name}#{" #{type}" if type}>"
|
|
189
|
+
else
|
|
190
|
+
"#{key}=#{value.inspect}"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
#: (Array[untyped]) -> String
|
|
195
|
+
def counted(items)
|
|
196
|
+
"#{items.length} #{items.length == 1 ? "item" : "items"}"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
#: (String) -> String?
|
|
200
|
+
def field_for(name)
|
|
201
|
+
return name if attributes.key?(name)
|
|
202
|
+
|
|
203
|
+
camelized = name.gsub(/_([a-z\d])/) { Regexp.last_match(1).to_s.upcase }
|
|
204
|
+
|
|
205
|
+
camelized if attributes.key?(camelized)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
#: (untyped, String) -> untyped
|
|
209
|
+
def wrap(value, key)
|
|
210
|
+
case value
|
|
211
|
+
when Hash
|
|
212
|
+
key == "loc" ? value : Node.new(value, self, key)
|
|
213
|
+
when Array
|
|
214
|
+
value.map { |item| wrap(item, key) }
|
|
215
|
+
else
|
|
216
|
+
value
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
data/lib/lightningcss/options.rb
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# What a stylesheet parsed to.
|
|
5
|
+
#
|
|
6
|
+
# `stylesheet` is the AST exactly as Lightning CSS serializes it, and `root` is the same tree as a
|
|
7
|
+
# `LightningCSS::Node` to walk. `warnings` are the ones a transform would have reported.
|
|
8
|
+
#
|
|
9
|
+
class ParseResult
|
|
10
|
+
attr_reader :source #: String?
|
|
11
|
+
attr_reader :stylesheet #: Hash[String, untyped]
|
|
12
|
+
attr_reader :warnings #: Array[String]
|
|
13
|
+
|
|
14
|
+
#: (String, ?String?) -> LightningCSS::ParseResult
|
|
15
|
+
def self.from_json(payload, source = nil)
|
|
16
|
+
parsed = JSON.parse(payload)
|
|
17
|
+
|
|
18
|
+
new(
|
|
19
|
+
source: source,
|
|
20
|
+
stylesheet: parsed.fetch("stylesheet"),
|
|
21
|
+
warnings: parsed.fetch("warnings")
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#: (stylesheet: Hash[String, untyped], warnings: Array[String], ?source: String?) -> void
|
|
26
|
+
def initialize(stylesheet:, warnings:, source: nil)
|
|
27
|
+
@source = source
|
|
28
|
+
@stylesheet = stylesheet.freeze
|
|
29
|
+
@warnings = warnings.freeze
|
|
30
|
+
|
|
31
|
+
freeze
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#: () -> LightningCSS::Node
|
|
35
|
+
def root
|
|
36
|
+
Node.new(stylesheet)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#: () -> bool
|
|
40
|
+
def warnings?
|
|
41
|
+
!warnings.empty?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#: () -> String
|
|
45
|
+
def inspect
|
|
46
|
+
parts = ["rules=#{stylesheet.fetch("rules").length}"]
|
|
47
|
+
parts << "warnings=#{warnings.inspect}" if warnings?
|
|
48
|
+
|
|
49
|
+
"#<#{self.class.name} #{parts.join(" ")}>"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -28,6 +28,11 @@ module LightningCSS
|
|
|
28
28
|
|
|
29
29
|
alias call transform
|
|
30
30
|
|
|
31
|
+
#: (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
32
|
+
def parse(code, **overrides)
|
|
33
|
+
LightningCSS.parse(code, **options.slice(*Options::PARSE), **overrides)
|
|
34
|
+
end
|
|
35
|
+
|
|
31
36
|
#: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
32
37
|
def bundle(path, **overrides)
|
|
33
38
|
LightningCSS.bundle(path, **options, **overrides)
|
data/lib/lightningcss/version.rb
CHANGED
|
@@ -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
|
data/lib/lightningcss.rb
CHANGED
|
@@ -15,6 +15,9 @@ end
|
|
|
15
15
|
|
|
16
16
|
require_relative "lightningcss/options"
|
|
17
17
|
require_relative "lightningcss/result"
|
|
18
|
+
require_relative "lightningcss/node"
|
|
19
|
+
require_relative "lightningcss/parse_result"
|
|
20
|
+
require_relative "lightningcss/visitor"
|
|
18
21
|
require_relative "lightningcss/transformer"
|
|
19
22
|
|
|
20
23
|
# Ruby bindings for Lightning CSS.
|
|
@@ -28,14 +31,27 @@ require_relative "lightningcss/transformer"
|
|
|
28
31
|
# LightningCSS.transform(".title { color: red }", scope: "[data-scope-abc]").code
|
|
29
32
|
# #=> ".title[data-scope-abc]{color:red}"
|
|
30
33
|
#
|
|
34
|
+
# `parse` returns the AST instead, to walk with `LightningCSS::Node` and `LightningCSS::Visitor`:
|
|
35
|
+
#
|
|
36
|
+
# LightningCSS.parse(".a { color: red }").root.every("declaration").first.property
|
|
37
|
+
# #=> "color"
|
|
38
|
+
#
|
|
31
39
|
# `LightningCSS::Options` is what the options are read by, and `LightningCSS::Transformer` holds a
|
|
32
40
|
# set of them to reuse across many stylesheets.
|
|
41
|
+
#
|
|
33
42
|
module LightningCSS
|
|
34
43
|
#: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
35
44
|
def self.transform(code, **options)
|
|
36
45
|
Result.from_json(Backend.transform(code.to_s, Options.serialize(options)))
|
|
37
46
|
end
|
|
38
47
|
|
|
48
|
+
#: (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
49
|
+
def self.parse(code, **options)
|
|
50
|
+
serialized = Options.serialize(options, Options::PARSE, "a parse")
|
|
51
|
+
|
|
52
|
+
ParseResult.from_json(Backend.parse(code.to_s, serialized), code.to_s)
|
|
53
|
+
end
|
|
54
|
+
|
|
39
55
|
#: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
40
56
|
def self.bundle(path, **options)
|
|
41
57
|
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"
|
|
@@ -139,9 +142,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|
|
139
142
|
|
|
140
143
|
[[package]]
|
|
141
144
|
name = "cbindgen"
|
|
142
|
-
version = "0.
|
|
145
|
+
version = "0.29.4"
|
|
143
146
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
144
|
-
checksum = "
|
|
147
|
+
checksum = "2ecb53484c9c167ba674026b656d8a27d7657a58e6066aa902bfb1a4aa00ae20"
|
|
145
148
|
dependencies = [
|
|
146
149
|
"clap",
|
|
147
150
|
"heck",
|
|
@@ -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
|
|
|
@@ -425,9 +429,9 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
|
|
425
429
|
|
|
426
430
|
[[package]]
|
|
427
431
|
name = "heck"
|
|
428
|
-
version = "0.
|
|
432
|
+
version = "0.5.0"
|
|
429
433
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
430
|
-
checksum = "
|
|
434
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
431
435
|
|
|
432
436
|
[[package]]
|
|
433
437
|
name = "indexmap"
|
|
@@ -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]]
|
|
@@ -527,7 +532,7 @@ dependencies = [
|
|
|
527
532
|
|
|
528
533
|
[[package]]
|
|
529
534
|
name = "lightningcss-ffi"
|
|
530
|
-
version = "0.
|
|
535
|
+
version = "0.0.0"
|
|
531
536
|
dependencies = [
|
|
532
537
|
"cbindgen",
|
|
533
538
|
"lightningcss",
|
|
@@ -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]]
|
|
@@ -963,11 +970,11 @@ dependencies = [
|
|
|
963
970
|
|
|
964
971
|
[[package]]
|
|
965
972
|
name = "serde_spanned"
|
|
966
|
-
version = "
|
|
973
|
+
version = "1.1.1"
|
|
967
974
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
968
|
-
checksum = "
|
|
975
|
+
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
|
|
969
976
|
dependencies = [
|
|
970
|
-
"
|
|
977
|
+
"serde_core",
|
|
971
978
|
]
|
|
972
979
|
|
|
973
980
|
[[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"
|
|
@@ -1078,44 +1110,42 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|
|
1078
1110
|
|
|
1079
1111
|
[[package]]
|
|
1080
1112
|
name = "toml"
|
|
1081
|
-
version = "0.
|
|
1113
|
+
version = "0.9.12+spec-1.1.0"
|
|
1082
1114
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1083
|
-
checksum = "
|
|
1115
|
+
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
|
|
1084
1116
|
dependencies = [
|
|
1085
|
-
"
|
|
1117
|
+
"indexmap",
|
|
1118
|
+
"serde_core",
|
|
1086
1119
|
"serde_spanned",
|
|
1087
1120
|
"toml_datetime",
|
|
1088
|
-
"
|
|
1121
|
+
"toml_parser",
|
|
1122
|
+
"toml_writer",
|
|
1123
|
+
"winnow 0.7.15",
|
|
1089
1124
|
]
|
|
1090
1125
|
|
|
1091
1126
|
[[package]]
|
|
1092
1127
|
name = "toml_datetime"
|
|
1093
|
-
version = "0.
|
|
1128
|
+
version = "0.7.5+spec-1.1.0"
|
|
1094
1129
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1095
|
-
checksum = "
|
|
1130
|
+
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
|
1096
1131
|
dependencies = [
|
|
1097
|
-
"
|
|
1132
|
+
"serde_core",
|
|
1098
1133
|
]
|
|
1099
1134
|
|
|
1100
1135
|
[[package]]
|
|
1101
|
-
name = "
|
|
1102
|
-
version = "
|
|
1136
|
+
name = "toml_parser"
|
|
1137
|
+
version = "1.1.3+spec-1.1.0"
|
|
1103
1138
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1104
|
-
checksum = "
|
|
1139
|
+
checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
|
|
1105
1140
|
dependencies = [
|
|
1106
|
-
"
|
|
1107
|
-
"serde",
|
|
1108
|
-
"serde_spanned",
|
|
1109
|
-
"toml_datetime",
|
|
1110
|
-
"toml_write",
|
|
1111
|
-
"winnow",
|
|
1141
|
+
"winnow 1.0.4",
|
|
1112
1142
|
]
|
|
1113
1143
|
|
|
1114
1144
|
[[package]]
|
|
1115
|
-
name = "
|
|
1116
|
-
version = "
|
|
1145
|
+
name = "toml_writer"
|
|
1146
|
+
version = "1.1.2+spec-1.1.0"
|
|
1117
1147
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1118
|
-
checksum = "
|
|
1148
|
+
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
|
1119
1149
|
|
|
1120
1150
|
[[package]]
|
|
1121
1151
|
name = "unicode-ident"
|
|
@@ -1237,9 +1267,12 @@ name = "winnow"
|
|
|
1237
1267
|
version = "0.7.15"
|
|
1238
1268
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1239
1269
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1270
|
+
|
|
1271
|
+
[[package]]
|
|
1272
|
+
name = "winnow"
|
|
1273
|
+
version = "1.0.4"
|
|
1274
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1275
|
+
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
|
1243
1276
|
|
|
1244
1277
|
[[package]]
|
|
1245
1278
|
name = "wit-bindgen"
|
data/rust/Cargo.toml
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[package]
|
|
4
4
|
name = "lightningcss-ffi"
|
|
5
|
-
version = "0.2.0"
|
|
6
5
|
edition = "2021"
|
|
7
6
|
authors = ["Marco Roth <marco.roth@intergga.ch>"]
|
|
8
7
|
description = "C FFI bindings for Lightning CSS, used by the lightningcss gem"
|
|
@@ -16,9 +15,9 @@ path = "src/lib.rs"
|
|
|
16
15
|
crate-type = ["cdylib", "staticlib", "rlib"]
|
|
17
16
|
|
|
18
17
|
[dependencies]
|
|
19
|
-
lightningcss = { version = "1.0.0-alpha.
|
|
18
|
+
lightningcss = { version = "=1.0.0-alpha.72", features = ["visitor", "serde"] }
|
|
20
19
|
serde = { version = "1", features = ["derive"] }
|
|
21
20
|
serde_json = "1"
|
|
22
21
|
|
|
23
22
|
[build-dependencies]
|
|
24
|
-
cbindgen = "0.
|
|
23
|
+
cbindgen = "0.29"
|
data/rust/build.rs
CHANGED
|
@@ -28,10 +28,32 @@ fn main() {
|
|
|
28
28
|
PathBuf::from(&crate_dir).join("cbindgen.toml").display()
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
+
let version_rb_path = PathBuf::from(&crate_dir).join("../lib/lightningcss/version.rb");
|
|
32
|
+
|
|
33
|
+
println!("cargo:rerun-if-changed={}", version_rb_path.display());
|
|
34
|
+
|
|
31
35
|
println!(
|
|
32
36
|
"cargo:rustc-env=LIGHTNINGCSS_VERSION={}",
|
|
33
37
|
locked_version(&lock_path, "lightningcss")
|
|
34
38
|
);
|
|
39
|
+
|
|
40
|
+
println!("cargo:rustc-env=GEM_VERSION={}", gem_version(&version_rb_path));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn gem_version(version_rb_path: &PathBuf) -> String {
|
|
44
|
+
let Ok(source) = fs::read_to_string(version_rb_path) else {
|
|
45
|
+
return "unknown".to_string();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
for line in source.lines() {
|
|
49
|
+
let Some(rest) = line.trim().strip_prefix("VERSION = ") else {
|
|
50
|
+
continue;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return rest.trim().trim_matches('"').to_string();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
"unknown".to_string()
|
|
35
57
|
}
|
|
36
58
|
|
|
37
59
|
fn locked_version(lock_path: &PathBuf, package: &str) -> String {
|
data/rust/src/lib.rs
CHANGED
|
@@ -21,10 +21,10 @@ 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::{ParseResult, TransformOptions, TransformResult};
|
|
25
25
|
use crate::scope::Scoper;
|
|
26
26
|
|
|
27
|
-
pub const VERSION: &str = env!("
|
|
27
|
+
pub const VERSION: &str = env!("GEM_VERSION");
|
|
28
28
|
pub const LIGHTNINGCSS_VERSION: &str = env!("LIGHTNINGCSS_VERSION");
|
|
29
29
|
|
|
30
30
|
#[repr(C)]
|
|
@@ -125,6 +125,26 @@ 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 = StyleSheet::parse(code, parser_options).map_err(|error| Failure::parse(error.to_string()))?;
|
|
139
|
+
|
|
140
|
+
let warnings = collected
|
|
141
|
+
.read()
|
|
142
|
+
.map(|warnings| warnings.iter().map(|warning| warning.to_string()).collect())
|
|
143
|
+
.unwrap_or_default();
|
|
144
|
+
|
|
145
|
+
Ok(ParseResult { stylesheet, warnings })
|
|
146
|
+
}
|
|
147
|
+
|
|
128
148
|
fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformResult, Failure> {
|
|
129
149
|
let filename = options.filename.clone().unwrap_or_default();
|
|
130
150
|
let css_modules = match &options.css_modules {
|
|
@@ -295,7 +315,7 @@ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<Transfo
|
|
|
295
315
|
})
|
|
296
316
|
}
|
|
297
317
|
|
|
298
|
-
fn to_result(outcome: Result<
|
|
318
|
+
fn to_result(outcome: Result<impl serde::Serialize, Failure>) -> LightningCssResult {
|
|
299
319
|
match outcome {
|
|
300
320
|
Ok(result) => match serde_json::to_string(&result) {
|
|
301
321
|
Ok(json) => LightningCssResult::ok(json),
|
|
@@ -323,6 +343,21 @@ pub unsafe extern "C" fn lightningcss_transform(
|
|
|
323
343
|
to_result(transform_source(code, &options))
|
|
324
344
|
}
|
|
325
345
|
|
|
346
|
+
#[no_mangle]
|
|
347
|
+
pub unsafe extern "C" fn lightningcss_parse(code: *const c_char, options_json: *const c_char) -> LightningCssResult {
|
|
348
|
+
let code = match borrow_str(code, "code") {
|
|
349
|
+
Ok(code) => code,
|
|
350
|
+
Err(failure) => return LightningCssResult::err(failure),
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
let options = match borrow_options(options_json) {
|
|
354
|
+
Ok(options) => options,
|
|
355
|
+
Err(failure) => return LightningCssResult::err(failure),
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
to_result(parse_source(code, &options))
|
|
359
|
+
}
|
|
360
|
+
|
|
326
361
|
#[no_mangle]
|
|
327
362
|
pub unsafe extern "C" fn lightningcss_transform_style_attribute(
|
|
328
363
|
code: *const c_char,
|
data/rust/src/options.rs
CHANGED
|
@@ -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
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Generated from lib/lightningcss/node.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# One object in the AST Lightning CSS parsed a stylesheet to.
|
|
5
|
+
#
|
|
6
|
+
# root = LightningCSS.parse(".a { color: red }").root
|
|
7
|
+
#
|
|
8
|
+
# root.type #=> "stylesheet"
|
|
9
|
+
# root.every("declaration") #=> every declaration in the stylesheet
|
|
10
|
+
# root.rules.first.value.location #=> {"source_index" => 0, "line" => 0, "column" => 1}
|
|
11
|
+
#
|
|
12
|
+
# Every object in the tree is a node, so reads chain all the way down. Most say what they are
|
|
13
|
+
# with their own `type`. The stylesheet, a declaration block, and a declaration do not, so a node
|
|
14
|
+
# names those from where it sits.
|
|
15
|
+
class Node
|
|
16
|
+
include Enumerable[LightningCSS::Node]
|
|
17
|
+
|
|
18
|
+
POSITION: Array[String]
|
|
19
|
+
|
|
20
|
+
DECLARATIONS: Array[String]
|
|
21
|
+
|
|
22
|
+
attr_reader parent: LightningCSS::Node?
|
|
23
|
+
|
|
24
|
+
attr_reader field: String?
|
|
25
|
+
|
|
26
|
+
# : (Hash[String, untyped], ?LightningCSS::Node?, ?String?) -> void
|
|
27
|
+
def initialize: (Hash[String, untyped], ?LightningCSS::Node?, ?String?) -> void
|
|
28
|
+
|
|
29
|
+
# : () -> String?
|
|
30
|
+
def type: () -> String?
|
|
31
|
+
|
|
32
|
+
# : () -> String?
|
|
33
|
+
def underscored_type: () -> String?
|
|
34
|
+
|
|
35
|
+
# : () -> bool
|
|
36
|
+
def rule?: () -> bool
|
|
37
|
+
|
|
38
|
+
# : () -> Hash[String, Integer]?
|
|
39
|
+
def location: () -> Hash[String, Integer]?
|
|
40
|
+
|
|
41
|
+
# : (String) -> untyped
|
|
42
|
+
def []: (String) -> untyped
|
|
43
|
+
|
|
44
|
+
# : () -> Array[String]
|
|
45
|
+
def keys: () -> Array[String]
|
|
46
|
+
|
|
47
|
+
# : () -> Hash[String, untyped]
|
|
48
|
+
def to_h: () -> Hash[String, untyped]
|
|
49
|
+
|
|
50
|
+
# : (?untyped) -> String
|
|
51
|
+
def to_json: (?untyped) -> String
|
|
52
|
+
|
|
53
|
+
# : (Array[Symbol]?) -> Hash[Symbol, untyped]
|
|
54
|
+
def deconstruct_keys: (Array[Symbol]?) -> Hash[Symbol, untyped]
|
|
55
|
+
|
|
56
|
+
# : () -> Array[LightningCSS::Node]
|
|
57
|
+
def child_nodes: () -> Array[LightningCSS::Node]
|
|
58
|
+
|
|
59
|
+
# : () { (LightningCSS::Node) -> void } -> void
|
|
60
|
+
# : () -> Enumerator[LightningCSS::Node, void]
|
|
61
|
+
def each: () { (LightningCSS::Node) -> void } -> void
|
|
62
|
+
| () -> Enumerator[LightningCSS::Node, void]
|
|
63
|
+
|
|
64
|
+
# : () -> Array[LightningCSS::Node]
|
|
65
|
+
def ancestors: () -> Array[LightningCSS::Node]
|
|
66
|
+
|
|
67
|
+
# : (String) -> Array[LightningCSS::Node]
|
|
68
|
+
def every: (String) -> Array[LightningCSS::Node]
|
|
69
|
+
|
|
70
|
+
# : () -> Hash[String, untyped]
|
|
71
|
+
def fields: () -> Hash[String, untyped]
|
|
72
|
+
|
|
73
|
+
# : () -> String
|
|
74
|
+
def inspect: () -> String
|
|
75
|
+
|
|
76
|
+
# : (Symbol, *untyped) -> untyped
|
|
77
|
+
def method_missing: (Symbol, *untyped) -> untyped
|
|
78
|
+
|
|
79
|
+
# : (Symbol, ?bool) -> bool
|
|
80
|
+
def respond_to_missing?: (Symbol, ?bool) -> bool
|
|
81
|
+
|
|
82
|
+
attr_reader attributes: Hash[String, untyped]
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# : () -> String?
|
|
87
|
+
def inferred_type: () -> String?
|
|
88
|
+
|
|
89
|
+
# : (untyped, String) -> Array[LightningCSS::Node]
|
|
90
|
+
def nodes_in: (untyped, String) -> Array[LightningCSS::Node]
|
|
91
|
+
|
|
92
|
+
# : (String, untyped) -> String
|
|
93
|
+
def described_field: (String, untyped) -> String
|
|
94
|
+
|
|
95
|
+
# : (Array[untyped]) -> String
|
|
96
|
+
def counted: (Array[untyped]) -> String
|
|
97
|
+
|
|
98
|
+
# : (String) -> String?
|
|
99
|
+
def field_for: (String) -> String?
|
|
100
|
+
|
|
101
|
+
# : (untyped, String) -> untyped
|
|
102
|
+
def wrap: (untyped, String) -> untyped
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Generated from lib/lightningcss/parse_result.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# What a stylesheet parsed to.
|
|
5
|
+
#
|
|
6
|
+
# `stylesheet` is the AST exactly as Lightning CSS serializes it, and `root` is the same tree as a
|
|
7
|
+
# `LightningCSS::Node` to walk. `warnings` are the ones a transform would have reported.
|
|
8
|
+
class ParseResult
|
|
9
|
+
attr_reader source: String?
|
|
10
|
+
|
|
11
|
+
attr_reader stylesheet: Hash[String, untyped]
|
|
12
|
+
|
|
13
|
+
attr_reader warnings: Array[String]
|
|
14
|
+
|
|
15
|
+
# : (String, ?String?) -> LightningCSS::ParseResult
|
|
16
|
+
def self.from_json: (String, ?String?) -> LightningCSS::ParseResult
|
|
17
|
+
|
|
18
|
+
# : (stylesheet: Hash[String, untyped], warnings: Array[String], ?source: String?) -> void
|
|
19
|
+
def initialize: (stylesheet: Hash[String, untyped], warnings: Array[String], ?source: String?) -> void
|
|
20
|
+
|
|
21
|
+
# : () -> LightningCSS::Node
|
|
22
|
+
def root: () -> LightningCSS::Node
|
|
23
|
+
|
|
24
|
+
# : () -> bool
|
|
25
|
+
def warnings?: () -> bool
|
|
26
|
+
|
|
27
|
+
# : () -> String
|
|
28
|
+
def inspect: () -> String
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -21,6 +21,9 @@ module LightningCSS
|
|
|
21
21
|
|
|
22
22
|
alias call transform
|
|
23
23
|
|
|
24
|
+
# : (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
25
|
+
def parse: (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
26
|
+
|
|
24
27
|
# : (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
25
28
|
def bundle: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
26
29
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated from lib/lightningcss/visitor.rb with RBS::Inline
|
|
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
|
+
class Visitor
|
|
18
|
+
# : (LightningCSS::Node | LightningCSS::ParseResult) -> void
|
|
19
|
+
def visit: (LightningCSS::Node | LightningCSS::ParseResult) -> void
|
|
20
|
+
|
|
21
|
+
# : (LightningCSS::Node) -> void
|
|
22
|
+
def visit_children: (LightningCSS::Node) -> void
|
|
23
|
+
end
|
|
24
|
+
end
|
data/sig/lightningcss.rbs
CHANGED
|
@@ -11,12 +11,20 @@
|
|
|
11
11
|
# LightningCSS.transform(".title { color: red }", scope: "[data-scope-abc]").code
|
|
12
12
|
# #=> ".title[data-scope-abc]{color:red}"
|
|
13
13
|
#
|
|
14
|
+
# `parse` returns the AST instead, to walk with `LightningCSS::Node` and `LightningCSS::Visitor`:
|
|
15
|
+
#
|
|
16
|
+
# LightningCSS.parse(".a { color: red }").root.every("declaration").first.property
|
|
17
|
+
# #=> "color"
|
|
18
|
+
#
|
|
14
19
|
# `LightningCSS::Options` is what the options are read by, and `LightningCSS::Transformer` holds a
|
|
15
20
|
# set of them to reuse across many stylesheets.
|
|
16
21
|
module LightningCSS
|
|
17
22
|
# : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
18
23
|
def self.transform: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
19
24
|
|
|
25
|
+
# : (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
26
|
+
def self.parse: (String, ?filename: String?, ?error_recovery: bool) -> LightningCSS::ParseResult
|
|
27
|
+
|
|
20
28
|
# : (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
21
29
|
def self.bundle: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
|
|
22
30
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lightningcss
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.33.0.1
|
|
5
5
|
platform: arm-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Ruby bindings for Lightning CSS, an extremely fast CSS parser, transformer,
|
|
14
14
|
bundler, and minifier.
|
|
@@ -30,10 +30,13 @@ files:
|
|
|
30
30
|
- lib/lightningcss/4.0/lightningcss.so
|
|
31
31
|
- lib/lightningcss/backend.rb
|
|
32
32
|
- lib/lightningcss/errors.rb
|
|
33
|
+
- lib/lightningcss/node.rb
|
|
33
34
|
- lib/lightningcss/options.rb
|
|
35
|
+
- lib/lightningcss/parse_result.rb
|
|
34
36
|
- lib/lightningcss/result.rb
|
|
35
37
|
- lib/lightningcss/transformer.rb
|
|
36
38
|
- lib/lightningcss/version.rb
|
|
39
|
+
- lib/lightningcss/visitor.rb
|
|
37
40
|
- licenses/README.md
|
|
38
41
|
- licenses/lightningcss-MPL-2.0.txt
|
|
39
42
|
- lightningcss.gemspec
|
|
@@ -48,11 +51,14 @@ files:
|
|
|
48
51
|
- sig/lightningcss.rbs
|
|
49
52
|
- sig/lightningcss/backend.rbs
|
|
50
53
|
- sig/lightningcss/errors.rbs
|
|
54
|
+
- sig/lightningcss/node.rbs
|
|
51
55
|
- sig/lightningcss/options.rbs
|
|
56
|
+
- sig/lightningcss/parse_result.rbs
|
|
52
57
|
- sig/lightningcss/result.rbs
|
|
53
58
|
- sig/lightningcss/transformer.rbs
|
|
54
59
|
- sig/lightningcss/types.rbs
|
|
55
60
|
- sig/lightningcss/version.rbs
|
|
61
|
+
- sig/lightningcss/visitor.rbs
|
|
56
62
|
homepage: https://github.com/marcoroth/lightningcss-ruby
|
|
57
63
|
licenses:
|
|
58
64
|
- MIT
|