lightningcss 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac2ebda8558d9c647ba99bb0ae17dc67c33f4366a17a565b3cf42ab9c2964a95
4
- data.tar.gz: 49f4e08880cfd13d8fef4a4cdd5b17ed8dc75d95c69e955a55814e9ee4d32b89
3
+ metadata.gz: 0bf26de1f9cfbbb2f49f77242f9597c790753ee59f0d570a9a7a42af2786f546
4
+ data.tar.gz: 41dcbf4cbdce9e38934fe52b8e594e07cb6c1aa771739309388891597b6f32f8
5
5
  SHA512:
6
- metadata.gz: a66d61dba36255d63e2bc53633fabd9fc1dc3f3bca456b53c118dcac5421a38ce06930a3e7992d0f7d0c488a16b15ddcd97ccf3d6219cbace4f1dee0befe77fa
7
- data.tar.gz: '0709f1ebb8aa6d2f807f1d8593db905d744f39bd9535dcfecf006d13535cf926390120203ca903fc3be651e96ecdb51587f63ddd2c9088b41c09c3d1166aa9c9'
6
+ metadata.gz: b77a36fd6862d5bcb82d70660ed9a03bbeba0bfa9e817bc223d82670e4a47a159112f0b9956782c320df18835b7e4d011e7f32f4fecd571780ca0607c108744c
7
+ data.tar.gz: 531fa107ef8aceb5f33fb2da0d23c58a621428d8ddee329bc1d18a4bfc82d04759eec41bbb88d5cb048c16d3ef52f27ff236f1debf7898073f861954720d41bf
data/README.md CHANGED
@@ -62,6 +62,20 @@ LightningCSS.transform(".a { color: lab(50% 40 59) }", targets: { chrome: 80 },
62
62
  LightningCSS.bundle("app/assets/stylesheets/application.css", minify: true).code
63
63
  ```
64
64
 
65
+ A bundle names every file it reads by the path it read it from, so it takes no `filename` and says so when given one.
66
+
67
+ It answers the same result a transform does. Every warning names the file it came from, and compiling as a CSS module renames the names in every file it read while exporting the ones the entry wrote. A file it imported is hashed on its own, so its names never collide with the entry's.
68
+
69
+ ```ruby
70
+ result = LightningCSS.bundle("app/assets/stylesheets/application.css", css_modules: true)
71
+
72
+ result.exports
73
+ #=> {"application" => "_8Z4fiW_application"}
74
+
75
+ result.warnings.first
76
+ #=> "'deep' is not recognized as a valid pseudo-class. ... at app/assets/stylesheets/layout.css:0:9"
77
+ ```
78
+
65
79
  #### CSS modules
66
80
 
67
81
  Compiling as a CSS module renames every class, id, `@keyframes`, and custom identifier, and reports what each name became.
@@ -65,12 +65,12 @@ if target_platform
65
65
 
66
66
  system("rustup target add #{target_platform}") || warn("lightningcss: Failed to add Rust target #{target_platform}")
67
67
 
68
- cargo_args = "--release --target #{target_platform}"
68
+ cargo_args = "--release --locked --target #{target_platform}"
69
69
  lib_dir = File.join(target_dir, target_platform, "release")
70
70
  else
71
71
  puts "lightningcss: Compiling Rust library for native platform..."
72
72
 
73
- cargo_args = "--release"
73
+ cargo_args = "--release --locked"
74
74
  lib_dir = File.join(target_dir, "release")
75
75
  end
76
76
 
@@ -84,7 +84,18 @@ end
84
84
 
85
85
  static_lib = File.join(lib_dir, "liblightningcss_ffi.a")
86
86
 
87
- if File.exist?(static_lib)
87
+ developing = File.exist?(File.join(root_dir, ".git"))
88
+
89
+ if File.exist?(static_lib) && !developing
90
+ vendored = File.join(ext_dir, "liblightningcss_ffi.a")
91
+
92
+ FileUtils.cp(static_lib, vendored)
93
+ FileUtils.rm_rf(target_dir)
94
+
95
+ puts "lightningcss: Static library vendored at #{vendored}, Rust build directory removed"
96
+
97
+ $LDFLAGS << " #{vendored}"
98
+ elsif File.exist?(static_lib)
88
99
  puts "lightningcss: Static library found at #{static_lib}"
89
100
 
90
101
  $LDFLAGS << " #{static_lib}"
@@ -7,9 +7,18 @@
7
7
  #ifndef LIGHTNINGCSS_H
8
8
  #define LIGHTNINGCSS_H
9
9
 
10
+ typedef enum LightningCssErrorCode {
11
+ LIGHTNING_CSS_ERROR_CODE_NONE = 0,
12
+ LIGHTNING_CSS_ERROR_CODE_PARSE,
13
+ LIGHTNING_CSS_ERROR_CODE_OPTION,
14
+ LIGHTNING_CSS_ERROR_CODE_BUNDLE,
15
+ LIGHTNING_CSS_ERROR_CODE_INTERNAL,
16
+ } LightningCssErrorCode;
17
+
10
18
  typedef struct LightningCssResult {
11
19
  char *value;
12
20
  char *error;
21
+ enum LightningCssErrorCode code;
13
22
  } LightningCssResult;
14
23
 
15
24
  struct LightningCssResult lightningcss_transform(const char *code, const char *options_json);
@@ -21,6 +30,8 @@ struct LightningCssResult lightningcss_bundle(const char *path, const char *opti
21
30
 
22
31
  char *lightningcss_version(void);
23
32
 
33
+ char *lightningcss_lightningcss_version(void);
34
+
24
35
  void lightningcss_string_free(char *value);
25
36
 
26
37
  void lightningcss_result_free(struct LightningCssResult result);
@@ -1,5 +1,6 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/encoding.h>
3
+ #include <ruby/thread.h>
3
4
  #include "include/lightningcss.h"
4
5
 
5
6
  static VALUE rb_mLightningCSS;
@@ -9,6 +10,15 @@ static VALUE rb_eParseError;
9
10
  static VALUE rb_eOptionError;
10
11
  static VALUE rb_eBundleError;
11
12
 
13
+ typedef struct LightningCssResult (*lightningcss_function)(const char *, const char *);
14
+
15
+ struct call_arguments {
16
+ lightningcss_function function;
17
+ const char *input;
18
+ const char *options;
19
+ struct LightningCssResult result;
20
+ };
21
+
12
22
  static VALUE make_utf8_string(const char *cstring) {
13
23
  return rb_enc_str_new_cstr(cstring, rb_utf8_encoding());
14
24
  }
@@ -22,26 +32,19 @@ static VALUE take_utf8_string(char *cstring) {
22
32
  return string;
23
33
  }
24
34
 
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;
35
+ static VALUE error_class_for(enum LightningCssErrorCode code) {
36
+ switch (code) {
37
+ case LIGHTNING_CSS_ERROR_CODE_PARSE: return rb_eParseError;
38
+ case LIGHTNING_CSS_ERROR_CODE_OPTION: return rb_eOptionError;
39
+ case LIGHTNING_CSS_ERROR_CODE_BUNDLE: return rb_eBundleError;
40
+ default: return rb_eError;
36
41
  }
37
-
38
- return rb_eParseError;
39
42
  }
40
43
 
41
44
  static VALUE unwrap(struct LightningCssResult result) {
42
45
  if (result.error) {
43
46
  VALUE message = make_utf8_string(result.error);
44
- VALUE error_class = error_class_for(result.error);
47
+ VALUE error_class = error_class_for(result.code);
45
48
 
46
49
  lightningcss_result_free(result);
47
50
 
@@ -61,22 +64,42 @@ static VALUE unwrap(struct LightningCssResult result) {
61
64
  return value;
62
65
  }
63
66
 
67
+ static void *without_gvl(void *data) {
68
+ struct call_arguments *arguments = (struct call_arguments *) data;
69
+
70
+ arguments->result = arguments->function(arguments->input, arguments->options);
71
+
72
+ return NULL;
73
+ }
74
+
75
+ static VALUE call(lightningcss_function function, VALUE input, VALUE options) {
76
+ struct call_arguments arguments;
77
+
78
+ arguments.function = function;
79
+ arguments.input = StringValueCStr(input);
80
+ arguments.options = StringValueCStr(options);
81
+
82
+ rb_thread_call_without_gvl(without_gvl, &arguments, NULL, NULL);
83
+
84
+ return unwrap(arguments.result);
85
+ }
86
+
64
87
  static VALUE rb_transform(VALUE self, VALUE code, VALUE options) {
65
88
  (void) self;
66
89
 
67
- return unwrap(lightningcss_transform(StringValueCStr(code), StringValueCStr(options)));
90
+ return call(lightningcss_transform, code, options);
68
91
  }
69
92
 
70
93
  static VALUE rb_transform_style_attribute(VALUE self, VALUE code, VALUE options) {
71
94
  (void) self;
72
95
 
73
- return unwrap(lightningcss_transform_style_attribute(StringValueCStr(code), StringValueCStr(options)));
96
+ return call(lightningcss_transform_style_attribute, code, options);
74
97
  }
75
98
 
76
99
  static VALUE rb_bundle(VALUE self, VALUE path, VALUE options) {
77
100
  (void) self;
78
101
 
79
- return unwrap(lightningcss_bundle(StringValueCStr(path), StringValueCStr(options)));
102
+ return call(lightningcss_bundle, path, options);
80
103
  }
81
104
 
82
105
  static VALUE rb_native_version(VALUE self) {
@@ -85,6 +108,12 @@ static VALUE rb_native_version(VALUE self) {
85
108
  return take_utf8_string(lightningcss_version());
86
109
  }
87
110
 
111
+ static VALUE rb_lightningcss_version(VALUE self) {
112
+ (void) self;
113
+
114
+ return take_utf8_string(lightningcss_lightningcss_version());
115
+ }
116
+
88
117
  void Init_lightningcss(void) {
89
118
  rb_mLightningCSS = rb_define_module("LightningCSS");
90
119
  rb_mBackend = rb_define_module_under(rb_mLightningCSS, "Backend");
@@ -98,4 +127,5 @@ void Init_lightningcss(void) {
98
127
  rb_define_singleton_method(rb_mBackend, "transform_style_attribute", rb_transform_style_attribute, 2);
99
128
  rb_define_singleton_method(rb_mBackend, "bundle", rb_bundle, 2);
100
129
  rb_define_singleton_method(rb_mBackend, "version", rb_native_version, 0);
130
+ rb_define_singleton_method(rb_mBackend, "lightningcss_version", rb_lightningcss_version, 0);
101
131
  }
@@ -23,6 +23,11 @@ module LightningCSS
23
23
  unavailable(__method__)
24
24
  end
25
25
 
26
+ #: () -> String
27
+ def lightningcss_version
28
+ unavailable(__method__)
29
+ end
30
+
26
31
  private
27
32
 
28
33
  #: (Symbol?) -> bot
@@ -26,15 +26,23 @@ module LightningCSS
26
26
  :targets
27
27
  ].freeze #: Array[Symbol]
28
28
 
29
+ BUNDLE = [
30
+ :minify,
31
+ :error_recovery,
32
+ :targets,
33
+ :css_modules,
34
+ :scope
35
+ ].freeze #: Array[Symbol]
36
+
29
37
  attr_reader :to_h #: Hash[Symbol, untyped]
30
38
 
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
39
+ #: (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> String
40
+ def self.serialize(options, allowed = KNOWN, subject = "a transform")
41
+ new(options, allowed, subject).to_json
34
42
  end
35
43
 
36
- #: (?allowed: Array[Symbol], ?subject: String, **untyped) -> void
37
- def initialize(allowed: KNOWN, subject: "a transform", **options)
44
+ #: (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> void
45
+ def initialize(options, allowed = KNOWN, subject = "a transform")
38
46
  given = options.transform_keys(&:to_sym)
39
47
 
40
48
  validate!(given.keys, allowed, subject)
@@ -28,7 +28,7 @@ module LightningCSS
28
28
 
29
29
  alias call transform
30
30
 
31
- #: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
31
+ #: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
32
32
  def bundle(path, **overrides)
33
33
  LightningCSS.bundle(path, **options, **overrides)
34
34
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LightningCSS
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/lightningcss.rb CHANGED
@@ -36,14 +36,16 @@ module LightningCSS
36
36
  Result.from_json(Backend.transform(code.to_s, Options.serialize(options)))
37
37
  end
38
38
 
39
- #: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
39
+ #: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
40
40
  def self.bundle(path, **options)
41
- Result.from_json(Backend.bundle(path.to_s, Options.serialize(options)))
41
+ serialized = Options.serialize(options, Options::BUNDLE, "a bundle")
42
+
43
+ Result.from_json(Backend.bundle(path.to_s, serialized))
42
44
  end
43
45
 
44
46
  #: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?) -> LightningCSS::Result
45
47
  def self.transform_style_attribute(code, **options)
46
- serialized = Options.serialize(options, allowed: Options::STYLE_ATTRIBUTE, subject: "a style attribute")
48
+ serialized = Options.serialize(options, Options::STYLE_ATTRIBUTE, "a style attribute")
47
49
 
48
50
  Result.from_json(Backend.transform_style_attribute(code.to_s, serialized))
49
51
  end
@@ -54,7 +56,7 @@ module LightningCSS
54
56
  end
55
57
 
56
58
  #: () -> String
57
- def self.native_version
58
- Backend.version
59
+ def self.lightningcss_version
60
+ Backend.lightningcss_version
59
61
  end
60
62
  end
data/lightningcss.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = "An extremely fast CSS parser, transformer, bundler, and minifier."
12
12
  spec.description = "Ruby bindings for Lightning CSS, an extremely fast CSS parser, transformer, bundler, and minifier."
13
13
  spec.homepage = "https://github.com/marcoroth/lightningcss-ruby"
14
- spec.license = "MIT"
14
+ spec.licenses = ["MIT", "MPL-2.0"]
15
15
  spec.required_ruby_version = ">= 3.2.0"
16
16
  spec.require_paths = ["lib"]
17
17
 
data/rust/Cargo.lock CHANGED
@@ -527,7 +527,7 @@ dependencies = [
527
527
 
528
528
  [[package]]
529
529
  name = "lightningcss-ffi"
530
- version = "0.1.0"
530
+ version = "0.2.0"
531
531
  dependencies = [
532
532
  "cbindgen",
533
533
  "lightningcss",
data/rust/Cargo.toml CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [package]
4
4
  name = "lightningcss-ffi"
5
- version = "0.1.0"
5
+ version = "0.2.0"
6
6
  edition = "2021"
7
7
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
8
8
  description = "C FFI bindings for Lightning CSS, used by the lightningcss gem"
data/rust/build.rs CHANGED
@@ -1,4 +1,5 @@
1
1
  use std::env;
2
+ use std::fs;
2
3
  use std::path::PathBuf;
3
4
 
4
5
  fn main() {
@@ -12,4 +13,43 @@ fn main() {
12
13
 
13
14
  bindings.write_to_file(&header_path);
14
15
  }
16
+
17
+ let lock_path = PathBuf::from(&crate_dir).join("Cargo.lock");
18
+
19
+ println!("cargo:rerun-if-changed={}", lock_path.display());
20
+
21
+ println!(
22
+ "cargo:rerun-if-changed={}",
23
+ PathBuf::from(&crate_dir).join("src").display()
24
+ );
25
+
26
+ println!(
27
+ "cargo:rerun-if-changed={}",
28
+ PathBuf::from(&crate_dir).join("cbindgen.toml").display()
29
+ );
30
+
31
+ println!(
32
+ "cargo:rustc-env=LIGHTNINGCSS_VERSION={}",
33
+ locked_version(&lock_path, "lightningcss")
34
+ );
35
+ }
36
+
37
+ fn locked_version(lock_path: &PathBuf, package: &str) -> String {
38
+ let Ok(lock) = fs::read_to_string(lock_path) else {
39
+ return "unknown".to_string();
40
+ };
41
+
42
+ let mut lines = lock.lines();
43
+
44
+ while let Some(line) = lines.next() {
45
+ if line.trim() != format!("name = \"{package}\"") {
46
+ continue;
47
+ }
48
+
49
+ if let Some(version) = lines.next().and_then(|next| next.trim().strip_prefix("version = ")) {
50
+ return version.trim_matches('"').to_string();
51
+ }
52
+ }
53
+
54
+ "unknown".to_string()
15
55
  }
data/rust/cbindgen.toml CHANGED
@@ -10,6 +10,7 @@ no_includes = true
10
10
  [export]
11
11
  include = [
12
12
  "LightningCssResult",
13
+ "LightningCssErrorCode",
13
14
  ]
14
15
 
15
16
  [enum]
data/rust/src/lib.rs CHANGED
@@ -17,7 +17,7 @@ use std::os::raw::c_char;
17
17
  use std::ptr;
18
18
  use std::sync::{Arc, RwLock};
19
19
 
20
- use lightningcss::bundler::{Bundler, FileProvider};
20
+ use lightningcss::bundler::{BundleErrorKind, Bundler, FileProvider};
21
21
  use lightningcss::stylesheet::{MinifyOptions, ParserOptions, PrinterOptions, StyleAttribute, StyleSheet};
22
22
  use lightningcss::visitor::Visit;
23
23
 
@@ -25,11 +25,23 @@ use crate::options::{TransformOptions, TransformResult};
25
25
  use crate::scope::Scoper;
26
26
 
27
27
  pub const VERSION: &str = env!("CARGO_PKG_VERSION");
28
+ pub const LIGHTNINGCSS_VERSION: &str = env!("LIGHTNINGCSS_VERSION");
29
+
30
+ #[repr(C)]
31
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
32
+ pub enum LightningCssErrorCode {
33
+ None = 0,
34
+ Parse,
35
+ Option,
36
+ Bundle,
37
+ Internal,
38
+ }
28
39
 
29
40
  #[repr(C)]
30
41
  pub struct LightningCssResult {
31
42
  pub value: *mut c_char,
32
43
  pub error: *mut c_char,
44
+ pub code: LightningCssErrorCode,
33
45
  }
34
46
 
35
47
  impl LightningCssResult {
@@ -37,13 +49,50 @@ impl LightningCssResult {
37
49
  Self {
38
50
  value: into_c_string(value),
39
51
  error: ptr::null_mut(),
52
+ code: LightningCssErrorCode::None,
40
53
  }
41
54
  }
42
55
 
43
- fn err(message: impl AsRef<str>) -> Self {
56
+ fn err(failure: Failure) -> Self {
44
57
  Self {
45
58
  value: ptr::null_mut(),
46
- error: into_c_string(message.as_ref()),
59
+ error: into_c_string(failure.message),
60
+ code: failure.code,
61
+ }
62
+ }
63
+ }
64
+
65
+ pub struct Failure {
66
+ code: LightningCssErrorCode,
67
+ message: String,
68
+ }
69
+
70
+ impl Failure {
71
+ fn parse(message: impl Into<String>) -> Self {
72
+ Self {
73
+ code: LightningCssErrorCode::Parse,
74
+ message: message.into(),
75
+ }
76
+ }
77
+
78
+ fn option(message: impl Into<String>) -> Self {
79
+ Self {
80
+ code: LightningCssErrorCode::Option,
81
+ message: message.into(),
82
+ }
83
+ }
84
+
85
+ fn bundle(message: impl Into<String>) -> Self {
86
+ Self {
87
+ code: LightningCssErrorCode::Bundle,
88
+ message: message.into(),
89
+ }
90
+ }
91
+
92
+ fn internal(message: impl Into<String>) -> Self {
93
+ Self {
94
+ code: LightningCssErrorCode::Internal,
95
+ message: message.into(),
47
96
  }
48
97
  }
49
98
  }
@@ -52,17 +101,17 @@ fn into_c_string(value: impl Into<Vec<u8>>) -> *mut c_char {
52
101
  CString::new(value).unwrap_or_default().into_raw()
53
102
  }
54
103
 
55
- unsafe fn borrow_str<'a>(pointer: *const c_char, label: &str) -> Result<&'a str, String> {
104
+ unsafe fn borrow_str<'a>(pointer: *const c_char, label: &str) -> Result<&'a str, Failure> {
56
105
  if pointer.is_null() {
57
- return Err(format!("{label} is null"));
106
+ return Err(Failure::internal(format!("{label} is null")));
58
107
  }
59
108
 
60
109
  CStr::from_ptr(pointer)
61
110
  .to_str()
62
- .map_err(|error| format!("Invalid UTF-8 in {label}: {error}"))
111
+ .map_err(|error| Failure::internal(format!("Invalid UTF-8 in {label}: {error}")))
63
112
  }
64
113
 
65
- unsafe fn borrow_options(pointer: *const c_char) -> Result<TransformOptions, String> {
114
+ unsafe fn borrow_options(pointer: *const c_char) -> Result<TransformOptions, Failure> {
66
115
  if pointer.is_null() {
67
116
  return Ok(TransformOptions::default());
68
117
  }
@@ -73,13 +122,13 @@ unsafe fn borrow_options(pointer: *const c_char) -> Result<TransformOptions, Str
73
122
  return Ok(TransformOptions::default());
74
123
  }
75
124
 
76
- serde_json::from_str(json).map_err(|error| format!("Invalid options: {error}"))
125
+ serde_json::from_str(json).map_err(|error| Failure::option(format!("Invalid options: {error}")))
77
126
  }
78
127
 
79
- fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformResult, String> {
128
+ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformResult, Failure> {
80
129
  let filename = options.filename.clone().unwrap_or_default();
81
130
  let css_modules = match &options.css_modules {
82
- Some(modules) => Some(modules.to_config()?),
131
+ Some(modules) => Some(modules.to_config().map_err(Failure::option)?),
83
132
  None => None,
84
133
  };
85
134
 
@@ -93,14 +142,14 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
93
142
  ..ParserOptions::default()
94
143
  };
95
144
 
96
- let mut stylesheet = StyleSheet::parse(code, parser_options).map_err(|error| error.to_string())?;
145
+ let mut stylesheet = StyleSheet::parse(code, parser_options).map_err(|error| Failure::parse(error.to_string()))?;
97
146
 
98
147
  if let Some(fragment) = &options.scope {
99
- let mut scoper = Scoper::parse(fragment)?;
148
+ let mut scoper = Scoper::parse(fragment).map_err(Failure::option)?;
100
149
 
101
150
  stylesheet
102
151
  .visit(&mut scoper)
103
- .map_err(|error| format!("Failed to scope stylesheet: {error:?}"))?;
152
+ .map_err(|error| Failure::internal(format!("Failed to scope stylesheet: {error:?}")))?;
104
153
  }
105
154
 
106
155
  let targets = options.to_targets();
@@ -111,7 +160,7 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
111
160
  targets,
112
161
  ..MinifyOptions::default()
113
162
  })
114
- .map_err(|error| format!("Failed to minify: {error}"))?;
163
+ .map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
115
164
  }
116
165
 
117
166
  let printed = stylesheet
@@ -123,7 +172,7 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
123
172
  analyze_dependencies: None,
124
173
  pseudo_classes: None,
125
174
  })
126
- .map_err(|error| format!("Failed to print: {error}"))?;
175
+ .map_err(|error| Failure::internal(format!("Failed to print: {error}")))?;
127
176
 
128
177
  let exports = printed.exports.map(|exports| {
129
178
  exports
@@ -144,31 +193,37 @@ fn transform_source(code: &str, options: &TransformOptions) -> Result<TransformR
144
193
  })
145
194
  }
146
195
 
147
- fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResult, String> {
196
+ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResult, Failure> {
148
197
  let css_modules = match &options.css_modules {
149
- Some(modules) => Some(modules.to_config()?),
198
+ Some(modules) => Some(modules.to_config().map_err(Failure::option)?),
150
199
  None => None,
151
200
  };
152
201
 
202
+ let provider = FileProvider::new();
203
+ let collected = Arc::new(RwLock::new(Vec::new()));
204
+
153
205
  let parser_options = ParserOptions {
154
206
  css_modules,
155
207
  error_recovery: options.error_recovery,
208
+ warnings: Some(collected.clone()),
156
209
  ..ParserOptions::default()
157
210
  };
158
211
 
159
- let provider = FileProvider::new();
160
212
  let mut bundler = Bundler::new(&provider, None, parser_options);
161
213
 
162
214
  let mut stylesheet = bundler
163
215
  .bundle(std::path::Path::new(path))
164
- .map_err(|error| error.to_string())?;
216
+ .map_err(|error| match &error.kind {
217
+ BundleErrorKind::ParserError(_) => Failure::parse(error.to_string()),
218
+ _ => Failure::bundle(error.to_string()),
219
+ })?;
165
220
 
166
221
  if let Some(fragment) = &options.scope {
167
- let mut scoper = Scoper::parse(fragment)?;
222
+ let mut scoper = Scoper::parse(fragment).map_err(Failure::option)?;
168
223
 
169
224
  stylesheet
170
225
  .visit(&mut scoper)
171
- .map_err(|error| format!("Failed to scope stylesheet: {error:?}"))?;
226
+ .map_err(|error| Failure::internal(format!("Failed to scope stylesheet: {error:?}")))?;
172
227
  }
173
228
 
174
229
  let targets = options.to_targets();
@@ -179,7 +234,7 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
179
234
  targets,
180
235
  ..MinifyOptions::default()
181
236
  })
182
- .map_err(|error| format!("Failed to minify: {error}"))?;
237
+ .map_err(|error| Failure::internal(format!("Failed to minify: {error}")))?;
183
238
  }
184
239
 
185
240
  let printed = stylesheet
@@ -188,23 +243,35 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
188
243
  targets,
189
244
  ..PrinterOptions::default()
190
245
  })
191
- .map_err(|error| format!("Failed to print: {error}"))?;
246
+ .map_err(|error| Failure::internal(format!("Failed to print: {error}")))?;
247
+
248
+ let exports = printed.exports.map(|exports| {
249
+ exports
250
+ .into_iter()
251
+ .map(|(local, export)| (local, export.name))
252
+ .collect::<HashMap<String, String>>()
253
+ });
254
+
255
+ let warnings = collected
256
+ .read()
257
+ .map(|warnings| warnings.iter().map(|warning| warning.to_string()).collect())
258
+ .unwrap_or_default();
192
259
 
193
260
  Ok(TransformResult {
194
261
  code: printed.code,
195
- exports: None,
196
- warnings: Vec::new(),
262
+ exports,
263
+ warnings,
197
264
  })
198
265
  }
199
266
 
200
- fn transform_attribute(code: &str, options: &TransformOptions) -> Result<TransformResult, String> {
267
+ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<TransformResult, Failure> {
201
268
  let parser_options = ParserOptions {
202
269
  filename: options.filename.clone().unwrap_or_default(),
203
270
  error_recovery: options.error_recovery,
204
271
  ..ParserOptions::default()
205
272
  };
206
273
 
207
- let mut attribute = StyleAttribute::parse(code, parser_options).map_err(|error| error.to_string())?;
274
+ let mut attribute = StyleAttribute::parse(code, parser_options).map_err(|error| Failure::parse(error.to_string()))?;
208
275
 
209
276
  let targets = options.to_targets();
210
277
 
@@ -219,7 +286,7 @@ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<Transfo
219
286
  targets,
220
287
  ..PrinterOptions::default()
221
288
  })
222
- .map_err(|error| format!("Failed to print: {error}"))?;
289
+ .map_err(|error| Failure::internal(format!("Failed to print: {error}")))?;
223
290
 
224
291
  Ok(TransformResult {
225
292
  code: printed.code,
@@ -228,13 +295,13 @@ fn transform_attribute(code: &str, options: &TransformOptions) -> Result<Transfo
228
295
  })
229
296
  }
230
297
 
231
- fn to_result(outcome: Result<TransformResult, String>) -> LightningCssResult {
298
+ fn to_result(outcome: Result<TransformResult, Failure>) -> LightningCssResult {
232
299
  match outcome {
233
300
  Ok(result) => match serde_json::to_string(&result) {
234
301
  Ok(json) => LightningCssResult::ok(json),
235
- Err(error) => LightningCssResult::err(format!("Failed to serialize result: {error}")),
302
+ Err(error) => LightningCssResult::err(Failure::internal(format!("Failed to serialize result: {error}"))),
236
303
  },
237
- Err(message) => LightningCssResult::err(message),
304
+ Err(failure) => LightningCssResult::err(failure),
238
305
  }
239
306
  }
240
307
 
@@ -245,12 +312,12 @@ pub unsafe extern "C" fn lightningcss_transform(
245
312
  ) -> LightningCssResult {
246
313
  let code = match borrow_str(code, "code") {
247
314
  Ok(code) => code,
248
- Err(message) => return LightningCssResult::err(message),
315
+ Err(failure) => return LightningCssResult::err(failure),
249
316
  };
250
317
 
251
318
  let options = match borrow_options(options_json) {
252
319
  Ok(options) => options,
253
- Err(message) => return LightningCssResult::err(message),
320
+ Err(failure) => return LightningCssResult::err(failure),
254
321
  };
255
322
 
256
323
  to_result(transform_source(code, &options))
@@ -263,12 +330,12 @@ pub unsafe extern "C" fn lightningcss_transform_style_attribute(
263
330
  ) -> LightningCssResult {
264
331
  let code = match borrow_str(code, "code") {
265
332
  Ok(code) => code,
266
- Err(message) => return LightningCssResult::err(message),
333
+ Err(failure) => return LightningCssResult::err(failure),
267
334
  };
268
335
 
269
336
  let options = match borrow_options(options_json) {
270
337
  Ok(options) => options,
271
- Err(message) => return LightningCssResult::err(message),
338
+ Err(failure) => return LightningCssResult::err(failure),
272
339
  };
273
340
 
274
341
  to_result(transform_attribute(code, &options))
@@ -278,12 +345,12 @@ pub unsafe extern "C" fn lightningcss_transform_style_attribute(
278
345
  pub unsafe extern "C" fn lightningcss_bundle(path: *const c_char, options_json: *const c_char) -> LightningCssResult {
279
346
  let path = match borrow_str(path, "path") {
280
347
  Ok(path) => path,
281
- Err(message) => return LightningCssResult::err(message),
348
+ Err(failure) => return LightningCssResult::err(failure),
282
349
  };
283
350
 
284
351
  let options = match borrow_options(options_json) {
285
352
  Ok(options) => options,
286
- Err(message) => return LightningCssResult::err(message),
353
+ Err(failure) => return LightningCssResult::err(failure),
287
354
  };
288
355
 
289
356
  to_result(bundle_source(path, &options))
@@ -294,6 +361,11 @@ pub unsafe extern "C" fn lightningcss_version() -> *mut c_char {
294
361
  into_c_string(VERSION)
295
362
  }
296
363
 
364
+ #[no_mangle]
365
+ pub unsafe extern "C" fn lightningcss_lightningcss_version() -> *mut c_char {
366
+ into_c_string(LIGHTNINGCSS_VERSION)
367
+ }
368
+
297
369
  #[no_mangle]
298
370
  pub unsafe extern "C" fn lightningcss_string_free(value: *mut c_char) {
299
371
  if !value.is_null() {
data/rust/src/scope.rs CHANGED
@@ -4,8 +4,10 @@
4
4
  //! `parcel_selectors` stores a selector in reverse match order, so `Selector::append` lands the
5
5
  //! fragment in the last compound and before any pseudo-element, which is where a scope belongs:
6
6
  //!
7
- //! .card .title -> .card .title[data-herb-scope-abc]
8
- //! .item::before -> .item[data-herb-scope-abc]::before
7
+ //! ```text
8
+ //! .card .title -> .card .title[data-herb-scope-abc]
9
+ //! .item::before -> .item[data-herb-scope-abc]::before
10
+ //! ```
9
11
  //!
10
12
  //! A fragment is anything that parses as one compound selector, so an attribute and a `:where()`
11
13
  //! carrying its own alternatives are both expressible.
@@ -15,6 +15,9 @@ module LightningCSS
15
15
  # : () -> String
16
16
  def version: () -> String
17
17
 
18
+ # : () -> String
19
+ def lightningcss_version: () -> String
20
+
18
21
  private
19
22
 
20
23
  # : (Symbol?) -> bot
@@ -13,13 +13,15 @@ module LightningCSS
13
13
 
14
14
  STYLE_ATTRIBUTE: Array[Symbol]
15
15
 
16
+ BUNDLE: Array[Symbol]
17
+
16
18
  attr_reader to_h: Hash[Symbol, untyped]
17
19
 
18
- # : (Hash[Symbol, untyped], ?allowed: Array[Symbol], ?subject: String) -> String
19
- def self.serialize: (Hash[Symbol, untyped], ?allowed: Array[Symbol], ?subject: String) -> String
20
+ # : (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> String
21
+ def self.serialize: (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> String
20
22
 
21
- # : (?allowed: Array[Symbol], ?subject: String, **untyped) -> void
22
- def initialize: (?allowed: Array[Symbol], ?subject: String, **untyped) -> void
23
+ # : (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> void
24
+ def initialize: (Hash[Symbol, untyped], ?Array[Symbol], ?String) -> void
23
25
 
24
26
  # : (?untyped) -> String
25
27
  def to_json: (?untyped) -> String
@@ -21,8 +21,8 @@ module LightningCSS
21
21
 
22
22
  alias call transform
23
23
 
24
- # : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
25
- def bundle: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
24
+ # : (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
25
+ def bundle: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
26
26
 
27
27
  # : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?) -> LightningCSS::Result
28
28
  def transform_style_attribute: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?) -> LightningCSS::Result
data/sig/lightningcss.rbs CHANGED
@@ -17,8 +17,8 @@ module LightningCSS
17
17
  # : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
18
18
  def self.transform: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
19
19
 
20
- # : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
21
- def self.bundle: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
20
+ # : (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
21
+ def self.bundle: (String, ?minify: bool, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> LightningCSS::Result
22
22
 
23
23
  # : (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?) -> LightningCSS::Result
24
24
  def self.transform_style_attribute: (String, ?filename: String?, ?minify: bool, ?error_recovery: bool, ?targets: browsers?) -> LightningCSS::Result
@@ -27,5 +27,5 @@ module LightningCSS
27
27
  def self.minify: (String, ?filename: String?, ?error_recovery: bool, ?targets: browsers?, ?css_modules: css_modules?, ?scope: String?) -> String
28
28
 
29
29
  # : () -> String
30
- def self.native_version: () -> String
30
+ def self.lightningcss_version: () -> String
31
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lightningcss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
@@ -52,6 +52,7 @@ files:
52
52
  homepage: https://github.com/marcoroth/lightningcss-ruby
53
53
  licenses:
54
54
  - MIT
55
+ - MPL-2.0
55
56
  metadata:
56
57
  homepage_uri: https://github.com/marcoroth/lightningcss-ruby
57
58
  source_code_uri: https://github.com/marcoroth/lightningcss-ruby