lightningcss 0.1.0-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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +231 -0
- data/ext/lightningcss/extconf.rb +112 -0
- data/ext/lightningcss/include/lightningcss.h +28 -0
- data/ext/lightningcss/lightningcss.c +101 -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 +36 -0
- data/lib/lightningcss/errors.rb +8 -0
- data/lib/lightningcss/options.rb +82 -0
- data/lib/lightningcss/result.rb +57 -0
- data/lib/lightningcss/transformer.rb +56 -0
- data/lib/lightningcss/version.rb +5 -0
- data/lib/lightningcss.rb +60 -0
- data/licenses/README.md +12 -0
- data/licenses/lightningcss-MPL-2.0.txt +373 -0
- data/lightningcss.gemspec +43 -0
- data/rust/Cargo.lock +1283 -0
- data/rust/Cargo.toml +24 -0
- data/rust/build.rs +15 -0
- data/rust/cbindgen.toml +23 -0
- data/rust/rustfmt.toml +3 -0
- data/rust/src/lib.rs +308 -0
- data/rust/src/options.rs +98 -0
- data/rust/src/scope.rs +63 -0
- data/sig/lightningcss/backend.rbs +26 -0
- data/sig/lightningcss/errors.rbs +15 -0
- data/sig/lightningcss/options.rbs +38 -0
- data/sig/lightningcss/result.rbs +34 -0
- data/sig/lightningcss/transformer.rbs +39 -0
- data/sig/lightningcss/types.rbs +26 -0
- data/sig/lightningcss/version.rbs +5 -0
- data/sig/lightningcss.rbs +31 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 68cc2374c2bb0eea54e1a30712d302a560a6f731cdc56012058dff3b62b07413
|
|
4
|
+
data.tar.gz: fb5c3908ffb991bb51ae27b3c888ca8d1214c57dcd50127eeb3e94e6c6f6785c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4301d8b631775d46c9f167adca5b5668fb6e35d1ccee9473ad9396a1870d696e5eb08226fbba20fbfd3504e5816ed006a4ea76502fb0ea0accd30057bae3d6a1
|
|
7
|
+
data.tar.gz: 6d10fe592139616f30fe5477e1cc0db8f8d892726c5cac0a2de1e9881bc55f99f4a61042f581bb2c4c37722d671c6c716010b21dd2e7bdb9d7f267df0dbd180a
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marco Roth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
<h2 align="center">⚡ Lightning CSS for Ruby</h2>
|
|
2
|
+
|
|
3
|
+
<h4 align="center">An extremely fast CSS parser, transformer, bundler, and minifier.</h4>
|
|
4
|
+
|
|
5
|
+
<div align="center">Ruby bindings for <a href="https://lightningcss.dev">Lightning CSS</a>, the CSS toolchain written in Rust.</div><br/>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://rubygems.org/gems/lightningcss"><img alt="Gem Version" src="https://img.shields.io/gem/v/lightningcss"></a>
|
|
9
|
+
<a href="https://lightningcss.dev"><img alt="Documentation" src="https://img.shields.io/badge/lightningcss.dev-documentation-green"></a>
|
|
10
|
+
<a href="https://github.com/marcoroth/lightningcss-ruby/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/lightningcss-ruby"></a>
|
|
11
|
+
<a href="https://github.com/marcoroth/lightningcss-ruby/issues"><img alt="Issues" src="https://img.shields.io/github/issues/marcoroth/lightningcss-ruby"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<br/>
|
|
15
|
+
|
|
16
|
+
### What is Lightning CSS for Ruby?
|
|
17
|
+
|
|
18
|
+
Ruby bindings for [Lightning CSS](https://lightningcss.dev), an extremely fast CSS parser, transformer, bundler, and minifier written in Rust.
|
|
19
|
+
|
|
20
|
+
Everything here is Lightning CSS doing the work. For what the options mean and what it can do, [lightningcss.dev](https://lightningcss.dev) is the reference.
|
|
21
|
+
|
|
22
|
+
### Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle add lightningcss
|
|
26
|
+
```
|
|
27
|
+
|
|
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
|
+
|
|
30
|
+
### Usage
|
|
31
|
+
|
|
32
|
+
#### Transforming
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
LightningCSS.transform(".a { color: #ff0000 }", minify: true).code
|
|
36
|
+
#=> ".a{color:red}"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Minifying
|
|
40
|
+
|
|
41
|
+
`minify` answers the code directly, for when the rest of the result is not interesting.
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
LightningCSS.minify(".a { color: #ff0000 }")
|
|
45
|
+
#=> ".a{color:red}"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### Targeting browsers
|
|
49
|
+
|
|
50
|
+
Give the browsers you support, each as its major version. Anything they cannot read is lowered, with the original kept after it so that a browser which understands it still wins.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
LightningCSS.transform(".a { color: lab(50% 40 59) }", targets: { chrome: 80 }, minify: true).code
|
|
54
|
+
#=> ".a{color:#bf5702;color:lab(50% 40 59)}"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Bundling
|
|
58
|
+
|
|
59
|
+
`bundle` resolves the `@import` statements a stylesheet was written with by reading the files they name, so it takes a path on disk to the entry stylesheet.
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
LightningCSS.bundle("app/assets/stylesheets/application.css", minify: true).code
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### CSS modules
|
|
66
|
+
|
|
67
|
+
Compiling as a CSS module renames every class, id, `@keyframes`, and custom identifier, and reports what each name became.
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
result = LightningCSS.transform(".card { color: red }", css_modules: true, minify: true)
|
|
71
|
+
|
|
72
|
+
result.code
|
|
73
|
+
#=> "._8Z4fiW_card{color:red}"
|
|
74
|
+
|
|
75
|
+
result.exports
|
|
76
|
+
#=> {"card" => "_8Z4fiW_card"}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Pass a hash instead of `true` to say how:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
LightningCSS.transform(".card {}", css_modules: { pattern: "scoped-[local]" }).exports
|
|
83
|
+
#=> {"card" => "scoped-card"}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Scoping
|
|
87
|
+
|
|
88
|
+
`scope` narrows every rule by a selector fragment, so a stylesheet only applies where that fragment matches. The fragment lands on the last compound of each selector, which is where a scope belongs.
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
LightningCSS.transform(".card .title { color: red }", scope: "[data-scope-abc]", minify: true).code
|
|
92
|
+
#=> ".card .title[data-scope-abc]{color:red}"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Any single selector works as a fragment, so one carrying its own alternatives is fine:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
LightningCSS.transform(".title { color: red }", scope: ":where([s], [s] *)", minify: true).code
|
|
99
|
+
#=> ".title:where([s],[s] *){color:red}"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Keyframe selectors are left alone, and so is the inside of a functional pseudo class:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
LightningCSS.transform(".x:not(.y) { color: red }", scope: "[s]", minify: true).code
|
|
106
|
+
#=> ".x:not(.y)[s]{color:red}"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Style attributes
|
|
110
|
+
|
|
111
|
+
A style attribute is the list of declarations an element carries inline. It has no selectors and no at-rules around it, so it is its own grammar.
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
LightningCSS.transform_style_attribute("color: #ff0000; border: none", minify: true).code
|
|
115
|
+
#=> "color:red;border:none"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Having no selectors and no names, it takes neither `scope` nor `css_modules`, and says so when given one.
|
|
119
|
+
|
|
120
|
+
#### Reusing options
|
|
121
|
+
|
|
122
|
+
`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.
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
transformer = LightningCSS::Transformer.new(minify: true, targets: { chrome: 100 })
|
|
126
|
+
|
|
127
|
+
transformer.transform(".a { color: red }").code
|
|
128
|
+
transformer.transform(css, scope: "[data-scope-abc]").code
|
|
129
|
+
transformer.with(scope: "[s]")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
It answers `call` as well, so it can be handed to anything expecting something callable.
|
|
133
|
+
|
|
134
|
+
### Options
|
|
135
|
+
|
|
136
|
+
| Option | Type | Description |
|
|
137
|
+
|------------------|----------------|----------------------------------------------------------------------------------------|
|
|
138
|
+
| `filename` | `String` | The name to use in errors and warnings. |
|
|
139
|
+
| `minify` | `bool` | Whether to print the result as small as it goes. |
|
|
140
|
+
| `error_recovery` | `bool` | Whether to carry on past a rule it cannot read. Off by default, so such a rule raises. |
|
|
141
|
+
| `targets` | `Hash` | The browsers being compiled for, each as its major version. |
|
|
142
|
+
| `css_modules` | `bool`, `Hash` | Whether to compile as a CSS module, and how. |
|
|
143
|
+
| `scope` | `String` | A selector fragment to narrow every rule by. |
|
|
144
|
+
|
|
145
|
+
An option nobody reads is refused:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
LightningCSS.transform(".a {}", nonsense: true)
|
|
149
|
+
#=> LightningCSS::OptionError: Unknown option: nonsense
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Results
|
|
153
|
+
|
|
154
|
+
`transform`, `bundle`, and `transform_style_attribute` all answer a `LightningCSS::Result`.
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
result = LightningCSS.transform(".a { color: #ff0000 }", minify: true)
|
|
158
|
+
|
|
159
|
+
result.code
|
|
160
|
+
#=> ".a{color:red}"
|
|
161
|
+
|
|
162
|
+
result.to_s
|
|
163
|
+
#=> ".a{color:red}"
|
|
164
|
+
|
|
165
|
+
result.exports
|
|
166
|
+
#=> nil
|
|
167
|
+
|
|
168
|
+
result.warnings
|
|
169
|
+
#=> []
|
|
170
|
+
|
|
171
|
+
result.warnings?
|
|
172
|
+
#=> false
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`exports` is filled in when the stylesheet was compiled as a CSS module, and maps every name as it was written to the name it was compiled to:
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
result = LightningCSS.transform(".card { color: red }", css_modules: true, minify: true)
|
|
179
|
+
|
|
180
|
+
result.code
|
|
181
|
+
#=> "._8Z4fiW_card{color:red}"
|
|
182
|
+
|
|
183
|
+
result.exports
|
|
184
|
+
#=> {"card" => "_8Z4fiW_card"}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`warnings` holds what Lightning CSS understood well enough to keep but not well enough to act on. They are worth reading, because what they describe is kept as written and then does nothing:
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
result = LightningCSS.transform(".a:deep(.b) { color: red }", minify: true)
|
|
191
|
+
|
|
192
|
+
result.warnings?
|
|
193
|
+
#=> true
|
|
194
|
+
|
|
195
|
+
result.warnings.first
|
|
196
|
+
#=> "'deep' is not recognized as a valid pseudo-class. Did you mean '::deep' (pseudo-element) or is this a typo? at :0:9"
|
|
197
|
+
|
|
198
|
+
result.code
|
|
199
|
+
#=> ".a:deep(.b){color:red}"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Development
|
|
203
|
+
|
|
204
|
+
The gem is a C extension over a Rust crate. `rust/` builds a static library and generates the C header with [cbindgen](https://github.com/mozilla/cbindgen), `ext/lightningcss/` wraps it, and `lib/` is the Ruby API over that.
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
bin/setup
|
|
208
|
+
bundle exec rake
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`sig/` is generated from the `#:` annotations next to the code. Regenerate it with `rake rbs` after changing a signature, and CI checks that it matches.
|
|
212
|
+
|
|
213
|
+
### Acknowledgements
|
|
214
|
+
|
|
215
|
+
[Lightning CSS](https://lightningcss.dev) is written by [Devon Govett](https://github.com/devongovett) and maintained at [parcel-bundler/lightningcss](https://github.com/parcel-bundler/lightningcss). This gem only calls into it. Every CSS feature, browser target, and optimization comes from there.
|
|
216
|
+
|
|
217
|
+
Its selector engine, [`parcel_selectors`](https://github.com/parcel-bundler/lightningcss/tree/master/selectors), is a fork of the selector matching from [Servo](https://servo.org), by the Servo Project Developers.
|
|
218
|
+
|
|
219
|
+
Thank you to all of them.
|
|
220
|
+
|
|
221
|
+
### Contributing
|
|
222
|
+
|
|
223
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/marcoroth/lightningcss-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/marcoroth/lightningcss-ruby/blob/main/CODE_OF_CONDUCT.md).
|
|
224
|
+
|
|
225
|
+
Issues with CSS parsing, transforming, or minifying itself belong [upstream](https://github.com/parcel-bundler/lightningcss/issues), since this gem does none of that. Issues with the Ruby API, the build, or the bindings belong here.
|
|
226
|
+
|
|
227
|
+
### License
|
|
228
|
+
|
|
229
|
+
The Ruby, C, and Rust code in this gem is available under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
230
|
+
|
|
231
|
+
It builds against [Lightning CSS](https://github.com/parcel-bundler/lightningcss), which is licensed under the [MPL-2.0](https://www.mozilla.org/en-US/MPL/2.0/), and the native extension a precompiled gem ships has that code compiled into it. MPL-2.0 is a file-level copyleft license, so the source of the MPL-covered files stays available under the MPL. The upstream [LICENSE](https://github.com/parcel-bundler/lightningcss/blob/master/LICENSE) has the terms, and a copy travels with the gem in [`licenses/`](licenses) so that whoever received it has them in hand.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
ext_dir = __dir__
|
|
7
|
+
root_dir = File.expand_path("../..", ext_dir)
|
|
8
|
+
|
|
9
|
+
rust_dir = File.join(root_dir, "rust")
|
|
10
|
+
|
|
11
|
+
unless File.exist?(File.join(rust_dir, "Cargo.toml"))
|
|
12
|
+
abort <<~MESSAGE
|
|
13
|
+
|
|
14
|
+
ERROR: Rust sources not found at #{rust_dir}.
|
|
15
|
+
|
|
16
|
+
MESSAGE
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
unless system("cargo --version > /dev/null 2>&1")
|
|
20
|
+
abort <<~MESSAGE
|
|
21
|
+
|
|
22
|
+
ERROR: Rust toolchain not found.
|
|
23
|
+
|
|
24
|
+
lightningcss requires the Rust toolchain to compile from source.
|
|
25
|
+
|
|
26
|
+
Install Rust: https://rustup.rs
|
|
27
|
+
|
|
28
|
+
MESSAGE
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
RUST_TARGETS = {
|
|
32
|
+
"aarch64-linux-gnu" => "aarch64-unknown-linux-gnu",
|
|
33
|
+
"aarch64-linux-musl" => "aarch64-unknown-linux-musl",
|
|
34
|
+
"arm-linux-gnu" => "armv7-unknown-linux-gnueabihf",
|
|
35
|
+
"arm-linux-musl" => "armv7-unknown-linux-musleabihf",
|
|
36
|
+
"arm64-darwin" => "aarch64-apple-darwin",
|
|
37
|
+
"x86_64-darwin" => "x86_64-apple-darwin",
|
|
38
|
+
"x86_64-linux-gnu" => "x86_64-unknown-linux-gnu",
|
|
39
|
+
"x86_64-linux-musl" => "x86_64-unknown-linux-musl",
|
|
40
|
+
"x86-linux-gnu" => "i686-unknown-linux-gnu",
|
|
41
|
+
"x86-linux-musl" => "i686-unknown-linux-musl",
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
cross_compiling = ENV.key?("RUBY_CC_VERSION")
|
|
45
|
+
target_platform = ENV.fetch("CARGO_BUILD_TARGET", nil)
|
|
46
|
+
|
|
47
|
+
if cross_compiling && target_platform.nil?
|
|
48
|
+
rcd_platform = ENV.fetch("RCD_PLATFORM", "")
|
|
49
|
+
target_platform = RUST_TARGETS[rcd_platform]
|
|
50
|
+
|
|
51
|
+
if target_platform.nil?
|
|
52
|
+
ruby_platform = RbConfig::CONFIG["arch"]
|
|
53
|
+
target_platform = RUST_TARGETS.values.find { |target| ruby_platform.include?(target.split("-").first) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
header_path = File.join(ext_dir, "include", "lightningcss.h")
|
|
58
|
+
|
|
59
|
+
FileUtils.mkdir_p(File.dirname(header_path))
|
|
60
|
+
|
|
61
|
+
target_dir = File.join(rust_dir, "target")
|
|
62
|
+
|
|
63
|
+
if target_platform
|
|
64
|
+
puts "lightningcss: Cross-compiling Rust for target: #{target_platform}"
|
|
65
|
+
|
|
66
|
+
system("rustup target add #{target_platform}") || warn("lightningcss: Failed to add Rust target #{target_platform}")
|
|
67
|
+
|
|
68
|
+
cargo_args = "--release --target #{target_platform}"
|
|
69
|
+
lib_dir = File.join(target_dir, target_platform, "release")
|
|
70
|
+
else
|
|
71
|
+
puts "lightningcss: Compiling Rust library for native platform..."
|
|
72
|
+
|
|
73
|
+
cargo_args = "--release"
|
|
74
|
+
lib_dir = File.join(target_dir, "release")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
unless system("cd #{rust_dir} && cargo build #{cargo_args}")
|
|
78
|
+
abort "ERROR: Failed to compile lightningcss from Rust source."
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
unless File.exist?(header_path)
|
|
82
|
+
abort "ERROR: cbindgen did not generate #{header_path}. Try `cargo clean` in #{rust_dir} and reinstall."
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
static_lib = File.join(lib_dir, "liblightningcss_ffi.a")
|
|
86
|
+
|
|
87
|
+
if File.exist?(static_lib)
|
|
88
|
+
puts "lightningcss: Static library found at #{static_lib}"
|
|
89
|
+
|
|
90
|
+
$LDFLAGS << " #{static_lib}"
|
|
91
|
+
else
|
|
92
|
+
host_os = target_platform || RbConfig::CONFIG["host_os"]
|
|
93
|
+
|
|
94
|
+
lib_name = case host_os
|
|
95
|
+
when /darwin/ then "liblightningcss_ffi.dylib"
|
|
96
|
+
when /mingw|mswin|windows/ then "lightningcss_ffi.dll"
|
|
97
|
+
else "liblightningcss_ffi.so"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
lib_path = File.join(lib_dir, lib_name)
|
|
101
|
+
|
|
102
|
+
abort "ERROR: Shared library not found at #{lib_path}" unless File.exist?(lib_path)
|
|
103
|
+
|
|
104
|
+
puts "lightningcss: Shared library found at #{lib_path} (dynamic)"
|
|
105
|
+
|
|
106
|
+
$LDFLAGS << " -L#{lib_dir} -llightningcss_ffi"
|
|
107
|
+
$LDFLAGS << " -Wl,-rpath,#{lib_dir}" if RbConfig::CONFIG["host_os"].match?(/darwin|linux/)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
$CFLAGS << " -I#{ext_dir}"
|
|
111
|
+
|
|
112
|
+
create_makefile("lightningcss/lightningcss")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* Generated by cbindgen — do not edit manually */
|
|
2
|
+
|
|
3
|
+
#include <stdbool.h>
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <stddef.h>
|
|
6
|
+
|
|
7
|
+
#ifndef LIGHTNINGCSS_H
|
|
8
|
+
#define LIGHTNINGCSS_H
|
|
9
|
+
|
|
10
|
+
typedef struct LightningCssResult {
|
|
11
|
+
char *value;
|
|
12
|
+
char *error;
|
|
13
|
+
} LightningCssResult;
|
|
14
|
+
|
|
15
|
+
struct LightningCssResult lightningcss_transform(const char *code, const char *options_json);
|
|
16
|
+
|
|
17
|
+
struct LightningCssResult lightningcss_transform_style_attribute(const char *code,
|
|
18
|
+
const char *options_json);
|
|
19
|
+
|
|
20
|
+
struct LightningCssResult lightningcss_bundle(const char *path, const char *options_json);
|
|
21
|
+
|
|
22
|
+
char *lightningcss_version(void);
|
|
23
|
+
|
|
24
|
+
void lightningcss_string_free(char *value);
|
|
25
|
+
|
|
26
|
+
void lightningcss_result_free(struct LightningCssResult result);
|
|
27
|
+
|
|
28
|
+
#endif /* LIGHTNINGCSS_H */
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/encoding.h>
|
|
3
|
+
#include "include/lightningcss.h"
|
|
4
|
+
|
|
5
|
+
static VALUE rb_mLightningCSS;
|
|
6
|
+
static VALUE rb_mBackend;
|
|
7
|
+
static VALUE rb_eError;
|
|
8
|
+
static VALUE rb_eParseError;
|
|
9
|
+
static VALUE rb_eOptionError;
|
|
10
|
+
static VALUE rb_eBundleError;
|
|
11
|
+
|
|
12
|
+
static VALUE make_utf8_string(const char *cstring) {
|
|
13
|
+
return rb_enc_str_new_cstr(cstring, rb_utf8_encoding());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static VALUE take_utf8_string(char *cstring) {
|
|
17
|
+
if (!cstring) return Qnil;
|
|
18
|
+
|
|
19
|
+
VALUE string = make_utf8_string(cstring);
|
|
20
|
+
lightningcss_string_free(cstring);
|
|
21
|
+
|
|
22
|
+
return string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static VALUE error_class_for(const char *message) {
|
|
26
|
+
if (strstr(message, "Invalid options") || strstr(message, "Invalid CSS modules pattern")) {
|
|
27
|
+
return rb_eOptionError;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (strstr(message, "os error") || strstr(message, "No such file")) {
|
|
31
|
+
return rb_eBundleError;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (strstr(message, "Invalid scope selector") || strstr(message, "Scope selector")) {
|
|
35
|
+
return rb_eOptionError;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return rb_eParseError;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static VALUE unwrap(struct LightningCssResult result) {
|
|
42
|
+
if (result.error) {
|
|
43
|
+
VALUE message = make_utf8_string(result.error);
|
|
44
|
+
VALUE error_class = error_class_for(result.error);
|
|
45
|
+
|
|
46
|
+
lightningcss_result_free(result);
|
|
47
|
+
|
|
48
|
+
rb_raise(error_class, "%s", StringValueCStr(message));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!result.value) {
|
|
52
|
+
lightningcss_result_free(result);
|
|
53
|
+
|
|
54
|
+
rb_raise(rb_eError, "Lightning CSS returned no result");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
VALUE value = make_utf8_string(result.value);
|
|
58
|
+
|
|
59
|
+
lightningcss_result_free(result);
|
|
60
|
+
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static VALUE rb_transform(VALUE self, VALUE code, VALUE options) {
|
|
65
|
+
(void) self;
|
|
66
|
+
|
|
67
|
+
return unwrap(lightningcss_transform(StringValueCStr(code), StringValueCStr(options)));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static VALUE rb_transform_style_attribute(VALUE self, VALUE code, VALUE options) {
|
|
71
|
+
(void) self;
|
|
72
|
+
|
|
73
|
+
return unwrap(lightningcss_transform_style_attribute(StringValueCStr(code), StringValueCStr(options)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static VALUE rb_bundle(VALUE self, VALUE path, VALUE options) {
|
|
77
|
+
(void) self;
|
|
78
|
+
|
|
79
|
+
return unwrap(lightningcss_bundle(StringValueCStr(path), StringValueCStr(options)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static VALUE rb_native_version(VALUE self) {
|
|
83
|
+
(void) self;
|
|
84
|
+
|
|
85
|
+
return take_utf8_string(lightningcss_version());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
void Init_lightningcss(void) {
|
|
89
|
+
rb_mLightningCSS = rb_define_module("LightningCSS");
|
|
90
|
+
rb_mBackend = rb_define_module_under(rb_mLightningCSS, "Backend");
|
|
91
|
+
|
|
92
|
+
rb_eError = rb_define_class_under(rb_mLightningCSS, "Error", rb_eStandardError);
|
|
93
|
+
rb_eParseError = rb_define_class_under(rb_mLightningCSS, "ParseError", rb_eError);
|
|
94
|
+
rb_eOptionError = rb_define_class_under(rb_mLightningCSS, "OptionError", rb_eError);
|
|
95
|
+
rb_eBundleError = rb_define_class_under(rb_mLightningCSS, "BundleError", rb_eError);
|
|
96
|
+
|
|
97
|
+
rb_define_singleton_method(rb_mBackend, "transform", rb_transform, 2);
|
|
98
|
+
rb_define_singleton_method(rb_mBackend, "transform_style_attribute", rb_transform_style_attribute, 2);
|
|
99
|
+
rb_define_singleton_method(rb_mBackend, "bundle", rb_bundle, 2);
|
|
100
|
+
rb_define_singleton_method(rb_mBackend, "version", rb_native_version, 0);
|
|
101
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
module Backend
|
|
5
|
+
module Unavailable
|
|
6
|
+
#: (String, String) -> String
|
|
7
|
+
def transform(_code, _options_json)
|
|
8
|
+
unavailable(__method__)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
#: (String, String) -> String
|
|
12
|
+
def transform_style_attribute(_code, _options_json)
|
|
13
|
+
unavailable(__method__)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#: (String, String) -> String
|
|
17
|
+
def bundle(_path, _options_json)
|
|
18
|
+
unavailable(__method__)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#: () -> String
|
|
22
|
+
def version
|
|
23
|
+
unavailable(__method__)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
#: (Symbol?) -> bot
|
|
29
|
+
def unavailable(name)
|
|
30
|
+
raise NotImplementedError, "LightningCSS::Backend.#{name} is defined by the native extension, which did not load"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
extend Unavailable
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LightningCSS
|
|
4
|
+
# The options a call was given, on their way to the native library.
|
|
5
|
+
#
|
|
6
|
+
# Lightning CSS reads them as JSON, so this is where a Ruby hash becomes one, and where an option
|
|
7
|
+
# nobody knows is refused. Refusing early is the point: an option the native side does not read
|
|
8
|
+
# would otherwise be accepted and quietly do nothing.
|
|
9
|
+
#
|
|
10
|
+
# LightningCSS::Options.new(minify: true).to_json #=> "{\"minify\":true}"
|
|
11
|
+
#
|
|
12
|
+
class Options
|
|
13
|
+
KNOWN = [
|
|
14
|
+
:filename,
|
|
15
|
+
:minify,
|
|
16
|
+
:error_recovery,
|
|
17
|
+
:targets,
|
|
18
|
+
:css_modules,
|
|
19
|
+
:scope
|
|
20
|
+
].freeze #: Array[Symbol]
|
|
21
|
+
|
|
22
|
+
STYLE_ATTRIBUTE = [
|
|
23
|
+
:filename,
|
|
24
|
+
:minify,
|
|
25
|
+
:error_recovery,
|
|
26
|
+
:targets
|
|
27
|
+
].freeze #: Array[Symbol]
|
|
28
|
+
|
|
29
|
+
attr_reader :to_h #: Hash[Symbol, untyped]
|
|
30
|
+
|
|
31
|
+
#: (Hash[Symbol, untyped], ?allowed: Array[Symbol], ?subject: String) -> String
|
|
32
|
+
def self.serialize(options, allowed: KNOWN, subject: "a transform")
|
|
33
|
+
new(allowed: allowed, subject: subject, **options).to_json
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#: (?allowed: Array[Symbol], ?subject: String, **untyped) -> void
|
|
37
|
+
def initialize(allowed: KNOWN, subject: "a transform", **options)
|
|
38
|
+
given = options.transform_keys(&:to_sym)
|
|
39
|
+
|
|
40
|
+
validate!(given.keys, allowed, subject)
|
|
41
|
+
|
|
42
|
+
@to_h = normalize(given).freeze
|
|
43
|
+
|
|
44
|
+
freeze
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#: (?untyped) -> String
|
|
48
|
+
def to_json(state = nil)
|
|
49
|
+
JSON.generate(to_h, state)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#: () -> String
|
|
53
|
+
def inspect
|
|
54
|
+
"#<#{self.class.name} #{to_h.inspect}>"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
#: (Array[Symbol], Array[Symbol], String) -> void
|
|
60
|
+
def validate!(names, allowed, subject)
|
|
61
|
+
unknown = names - KNOWN
|
|
62
|
+
|
|
63
|
+
raise OptionError, "Unknown option#{"s" if unknown.length > 1}: #{unknown.join(", ")}" if unknown.any?
|
|
64
|
+
|
|
65
|
+
unsupported = names - allowed
|
|
66
|
+
|
|
67
|
+
return if unsupported.empty?
|
|
68
|
+
|
|
69
|
+
raise OptionError, "#{unsupported.join(", ")} #{unsupported.one? ? "is not an option" : "are not options"} for #{subject}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#: (Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
|
|
73
|
+
def normalize(options)
|
|
74
|
+
normalized = options.dup
|
|
75
|
+
|
|
76
|
+
normalized[:css_modules] = {} if normalized[:css_modules] == true
|
|
77
|
+
normalized.delete(:css_modules) if normalized[:css_modules] == false
|
|
78
|
+
|
|
79
|
+
normalized.compact
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|