Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ LightningCSS.transform(".a { color: lab(50% 40 59) }", targets: { chrome: 80 },
LightningCSS.bundle("app/assets/stylesheets/application.css", minify: true).code
```

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.

```ruby
result = LightningCSS.bundle("app/assets/stylesheets/application.css", css_modules: true)

result.exports
#=> {"application" => "_8Z4fiW_application"}

result.warnings.first
#=> "'deep' is not recognized as a valid pseudo-class. ... at app/assets/stylesheets/layout.css:0:9"
```

#### CSS modules

Compiling as a CSS module renames every class, id, `@keyframes`, and custom identifier, and reports what each name became.
Expand Down
21 changes: 18 additions & 3 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,16 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
None => None,
};

let provider = FileProvider::new();
let collected = Arc::new(RwLock::new(Vec::new()));

let parser_options = ParserOptions {
css_modules,
error_recovery: options.error_recovery,
warnings: Some(collected.clone()),
..ParserOptions::default()
};

let provider = FileProvider::new();
let mut bundler = Bundler::new(&provider, None, parser_options);

let mut stylesheet = bundler
Expand Down Expand Up @@ -191,10 +194,22 @@ fn bundle_source(path: &str, options: &TransformOptions) -> Result<TransformResu
})
.map_err(|error| format!("Failed to print: {error}"))?;

let exports = printed.exports.map(|exports| {
exports
.into_iter()
.map(|(local, export)| (local, export.name))
.collect::<HashMap<String, String>>()
});

let warnings = collected
.read()
.map(|warnings| warnings.iter().map(|warning| warning.to_string()).collect())
.unwrap_or_default();

Ok(TransformResult {
code: printed.code,
exports: None,
warnings: Vec::new(),
exports,
warnings,
})
}

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/bundle/nested/warned.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.a:deep(.b) { color: red; }
3 changes: 3 additions & 0 deletions test/fixtures/bundle/warned.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "nested/warned.css";

.warned { color: var(--brand); }
43 changes: 43 additions & 0 deletions test/lightningcss/bundle_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module LightningCSS
class BundleTest < Minitest::Spec
ENTRY = File.expand_path("../fixtures/bundle/entry.css", __dir__)
MISSING = File.expand_path("../fixtures/bundle/missing.css", __dir__)
WARNED = File.expand_path("../fixtures/bundle/warned.css", __dir__)
IMPORTED = File.expand_path("../fixtures/bundle/nested/warned.css", __dir__)
WARNING = "'deep' is not recognized as a valid pseudo-class. Did you mean '::deep' (pseudo-element) or is this a typo? at #{IMPORTED}:0:9".freeze

test "resolves the imports a stylesheet was written with, in the order it imported them" do
code = LightningCSS.bundle(ENTRY, minify: true).code
Expand Down Expand Up @@ -34,5 +37,45 @@ class BundleTest < Minitest::Spec

assert_equal ":root{--brand:red}.layout{display:grid}.entry{color:var(--brand)}", code
end

test "renames every name it bundled, and reports the ones the entry wrote" do
result = LightningCSS.bundle(ENTRY, css_modules: { pattern: "bundled-[local]" }, minify: true)

assert_equal ":root{--brand:red}.bundled-layout{display:grid}.bundled-entry{color:var(--brand)}", result.code
assert_equal({ "entry" => "bundled-entry" }, result.exports)
end

test "hashes every file it bundled on its own, so two of them never collide" do
code = LightningCSS.bundle(ENTRY, css_modules: true, minify: true).code

entry = code[/\.(\w+)_entry\{/, 1]
layout = code[/\.(\w+)_layout\{/, 1]

refute_nil entry
refute_nil layout
refute_equal entry, layout
end

test "reports no exports when it was not asked to compile a CSS module" do
assert_nil LightningCSS.bundle(ENTRY, minify: true).exports
end

test "reports what it kept but did not understand, and which file wrote it" do
result = LightningCSS.bundle(WARNED, minify: true)

assert_equal [WARNING], result.warnings
assert_predicate result, :warnings?
end

test "keeps the rule it warned about" do
assert_includes LightningCSS.bundle(WARNED, minify: true).code, ".a:deep(.b){color:red}"
end

test "reports none for a bundle it fully understood" do
result = LightningCSS.bundle(ENTRY, minify: true)

assert_empty result.warnings
refute_predicate result, :warnings?
end
end
end