diff --git a/bindings/Cargo.lock b/bindings/Cargo.lock index 3d00991fcb6b..791179ff23ce 100644 --- a/bindings/Cargo.lock +++ b/bindings/Cargo.lock @@ -253,6 +253,7 @@ name = "binding_html_node" version = "0.1.0" dependencies = [ "anyhow", + "lightningcss", "napi", "napi-build", "napi-derive", @@ -261,6 +262,10 @@ dependencies = [ "swc_atoms", "swc_cached", "swc_common", + "swc_css_ast", + "swc_css_codegen", + "swc_css_minifier", + "swc_css_parser", "swc_error_reporters", "swc_html", "swc_html_ast", @@ -403,7 +408,7 @@ dependencies = [ "chrono", "either", "indexmap 2.4.0", - "itertools", + "itertools 0.13.0", "nom", "once_cell", "serde", @@ -582,6 +587,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" +dependencies = [ + "const-str-proc-macro", +] + +[[package]] +name = "const-str-proc-macro" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -797,6 +822,38 @@ dependencies = [ "typenum", ] +[[package]] +name = "cssparser" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.2", + "smallvec", +] + +[[package]] +name = "cssparser-color" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" +dependencies = [ + "cssparser", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.75", +] + [[package]] name = "ctor" version = "0.2.8" @@ -895,6 +952,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "data-url" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" +dependencies = [ + "matches", +] + [[package]] name = "debugid" version = "0.8.0" @@ -975,6 +1041,21 @@ dependencies = [ "litrs", ] +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1386,7 +1467,7 @@ dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", - "phf", + "phf 0.11.2", "rustc-hash", "triomphe", ] @@ -1515,6 +1596,15 @@ dependencies = [ "syn 2.0.75", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1674,6 +1764,44 @@ dependencies = [ "libc", ] +[[package]] +name = "lightningcss" +version = "1.0.0-alpha.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec380ca49dc7f6a1cafbdd2de5e587958eac0f67ab26b1e56727fcc60a0c3d4d" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.6.0", + "const-str", + "cssparser", + "cssparser-color", + "dashmap", + "data-encoding", + "getrandom", + "itertools 0.10.5", + "lazy_static", + "lightningcss-derive", + "parcel_selectors", + "parcel_sourcemap", + "paste", + "pathdiff", + "rayon", + "serde", + "smallvec", +] + +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1765,6 +1893,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "memchr" version = "2.7.4" @@ -2071,6 +2205,36 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +[[package]] +name = "parcel_selectors" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512215cb1d3814e276ace4ec2dbc2cac16726ea3fcac20c22ae1197e16fdd72d" +dependencies = [ + "bitflags 2.6.0", + "cssparser", + "fxhash", + "log", + "phf 0.10.1", + "phf_codegen", + "precomputed-hash", + "smallvec", +] + +[[package]] +name = "parcel_sourcemap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" +dependencies = [ + "base64-simd", + "data-url", + "rkyv", + "serde", + "serde_json", + "vlq", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -2094,6 +2258,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "path-absolutize" version = "3.1.1" @@ -2146,6 +2316,15 @@ dependencies = [ "indexmap 2.4.0", ] +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + [[package]] name = "phf" version = "0.11.2" @@ -2153,7 +2332,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand", ] [[package]] @@ -2162,7 +2361,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", "rand", ] @@ -2172,13 +2371,22 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.2", + "phf_shared 0.11.2", "proc-macro2", "quote", "syn 2.0.75", ] +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.11.2" @@ -2235,6 +2443,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "preset_env_base" version = "0.5.1" @@ -3423,7 +3637,7 @@ dependencies = [ "bytecheck", "is-macro", "num-bigint", - "phf", + "phf 0.11.2", "rkyv", "scoped-tls", "serde", @@ -3664,7 +3878,7 @@ version = "0.120.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad03ee53c734eb74757d03c07ec71b1a982261830c9253ef3e2e4a089f9af25d" dependencies = [ - "phf", + "phf 0.11.2", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -3726,7 +3940,7 @@ dependencies = [ "num_cpus", "once_cell", "parking_lot", - "phf", + "phf 0.11.2", "radix_fmt", "rayon", "regex", @@ -3760,7 +3974,7 @@ dependencies = [ "new_debug_unreachable", "num-bigint", "num-traits", - "phf", + "phf 0.11.2", "serde", "smallvec", "smartstring", @@ -3827,7 +4041,7 @@ dependencies = [ "bitflags 2.6.0", "indexmap 2.4.0", "once_cell", - "phf", + "phf 0.11.2", "rayon", "rustc-hash", "serde", @@ -4148,7 +4362,6 @@ checksum = "706509020207c5e4dc17e9c6c3adcec901ec5e0d06314050643c3efce8be7fb7" dependencies = [ "swc_html_ast", "swc_html_codegen", - "swc_html_minifier", "swc_html_parser", "swc_html_visit", ] @@ -5077,6 +5290,12 @@ dependencies = [ "virtual-mio", ] +[[package]] +name = "vlq" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" + [[package]] name = "wai-bindgen-gen-core" version = "0.2.3" diff --git a/bindings/Cargo.toml b/bindings/Cargo.toml index 2e325bbde3ae..327483fb5f3e 100644 --- a/bindings/Cargo.toml +++ b/bindings/Cargo.toml @@ -14,6 +14,7 @@ resolver = "2" anyhow = "1.0.86" backtrace = "0.3" getrandom = "0.2.15" + lightningcss = "1.0.0-alpha.58" napi = { version = "2", default-features = false } napi-build = "2" napi-derive = { version = "2", default-features = false } @@ -30,6 +31,10 @@ resolver = "2" swc_compiler_base = "0.18.1" swc_config = "0.1.15" swc_core = "0.101.3" + swc_css_ast = "0.144.0" + swc_css_codegen = "0.155.0" + swc_css_minifier = "0.120.0" + swc_css_parser = "0.154.0" swc_error_reporters = "0.21.0" swc_fast_ts_strip = "0.6.1" swc_html = "0.148.0" diff --git a/bindings/binding_html_node/Cargo.toml b/bindings/binding_html_node/Cargo.toml index 8ebc5a72ac25..5be8779c1870 100644 --- a/bindings/binding_html_node/Cargo.toml +++ b/bindings/binding_html_node/Cargo.toml @@ -18,18 +18,25 @@ crate-type = ["cdylib"] napi-build = { workspace = true } [dependencies] -anyhow = { workspace = true } -napi = { workspace = true, features = ["napi3", "serde-json"] } -napi-derive = { workspace = true, features = ["type-def"] } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -swc_atoms = { workspace = true } -swc_cached = { workspace = true } -swc_common = { workspace = true, features = ["diagnostic-serde"] } +anyhow = { workspace = true } +lightningcss = { workspace = true } +napi = { workspace = true, features = ["napi3", "serde-json"] } +napi-derive = { workspace = true, features = ["type-def"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +swc_atoms = { workspace = true } +swc_cached = { workspace = true } +swc_common = { workspace = true, features = ["diagnostic-serde"] } +swc_css_ast = { workspace = true } +swc_css_codegen = { workspace = true } +swc_css_minifier = { workspace = true } +swc_css_parser = { workspace = true } swc_error_reporters = { workspace = true } -swc_html = { workspace = true, features = ["minifier"] } -swc_html_ast = { workspace = true, features = ["serde"] } -swc_html_minifier = { workspace = true } -swc_nodejs_common = { workspace = true } -tracing = { workspace = true, features = ["release_max_level_info"] } -tracing-subscriber = { workspace = true, features = ["env-filter"] } +swc_html = { workspace = true, default-features = false } +swc_html_ast = { workspace = true, features = ["serde"] } +swc_html_minifier = { workspace = true, default-features = false, features = [ + "custom-css-minifier", +] } +swc_nodejs_common = { workspace = true } +tracing = { workspace = true, features = ["release_max_level_info"] } +tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/bindings/binding_html_node/src/lib.rs b/bindings/binding_html_node/src/lib.rs index a576bcd24a82..473f1e6e8eb7 100644 --- a/bindings/binding_html_node/src/lib.rs +++ b/bindings/binding_html_node/src/lib.rs @@ -3,14 +3,19 @@ extern crate napi_derive; mod util; -use std::{backtrace::Backtrace, env, panic::set_hook}; +use std::{backtrace::Backtrace, borrow::Cow, env, panic::set_hook}; use anyhow::{bail, Context}; +use lightningcss::{ + printer::PrinterOptions, + stylesheet::{ParserFlags, StyleSheet}, + targets::Targets, +}; use napi::{bindgen_prelude::*, Task}; use serde::{Deserialize, Serialize}; use swc_atoms::js_word; use swc_cached::regex::CachedRegex; -use swc_common::{FileName, DUMMY_SP}; +use swc_common::{sync::Lrc, FileName, FilePathMapping, SourceMap, DUMMY_SP}; use swc_html::{ ast::{DocumentMode, Namespace}, codegen::{ @@ -21,11 +26,12 @@ use swc_html::{ }; use swc_html_ast::{Document, DocumentFragment}; use swc_html_minifier::{ - minify_document, minify_document_fragment, + minify_document_fragment_with_custom_css_minifier, minify_document_with_custom_css_minifier, option::{ - CollapseWhitespaces, CssOptions, MinifierType, MinifyCssOption, MinifyJsOption, - MinifyJsonOption, RemoveRedundantAttributes, + CollapseWhitespaces, MinifierType, MinifyCssOption, MinifyJsOption, MinifyJsonOption, + RemoveRedundantAttributes, }, + CssMinificationMode, MinifyCss, }; use swc_nodejs_common::{deserialize_json, get_deserialized, MapErr}; @@ -138,7 +144,7 @@ pub struct MinifyOptions { #[serde(default = "minify_js_by_default")] minify_js: MinifyJsOption, #[serde(default = "minify_css_by_default")] - minify_css: MinifyCssOption, + minify_css: MinifyCssOption, #[serde(default)] minify_additional_scripts_content: Option>, #[serde(default)] @@ -171,7 +177,7 @@ const fn minify_js_by_default() -> MinifyJsOption { MinifyJsOption::Bool(true) } -const fn minify_css_by_default() -> MinifyCssOption { +const fn minify_css_by_default() -> MinifyCssOption { MinifyCssOption::Bool(true) } @@ -264,6 +270,236 @@ fn create_element(context_element: Element) -> anyhow::Result, + data: String, + mode: swc_html_minifier::CssMinificationMode, + ) -> Option { + let opts = match options { + MinifyCssOption::Bool(v) => { + if *v { + Cow::Owned(CssMinfierOptions::LightningCss(LightningCssOptions {})) + } else { + return None; + } + } + MinifyCssOption::Options(opts) => Cow::Borrowed(opts), + }; + + match &*opts { + CssMinfierOptions::LightningCss(_) => { + let mut ss = StyleSheet::parse( + &data, + lightningcss::stylesheet::ParserOptions { + flags: lightningcss::stylesheet::ParserFlags::all(), + ..Default::default() + }, + ) + .ok()?; + + let targets = Targets::default(); + + ss.minify(lightningcss::stylesheet::MinifyOptions { + targets, + ..Default::default() + }) + .ok()?; + + let to_css_result = ss + .to_css(PrinterOptions { + minify: true, + targets, + ..Default::default() + }) + .ok()?; + + Some(to_css_result.code) + } + + CssMinfierOptions::Swc(options) => { + let mut options = options.clone(); + let mut errors: Vec<_> = Vec::new(); + + let cm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fm = cm.new_source_file(FileName::Anon.into(), data); + + let mut stylesheet = match mode { + CssMinificationMode::Stylesheet => { + match swc_css_parser::parse_file(&fm, None, options.parser, &mut errors) { + Ok(stylesheet) => stylesheet, + _ => return None, + } + } + CssMinificationMode::ListOfDeclarations => { + match swc_css_parser::parse_file::>( + &fm, + None, + options.parser, + &mut errors, + ) { + Ok(list_of_declarations) => { + let declaration_list: Vec = + list_of_declarations + .into_iter() + .map(|node| node.into()) + .collect(); + + swc_css_ast::Stylesheet { + span: Default::default(), + rules: vec![swc_css_ast::Rule::QualifiedRule( + swc_css_ast::QualifiedRule { + span: Default::default(), + prelude: + swc_css_ast::QualifiedRulePrelude::SelectorList( + swc_css_ast::SelectorList { + span: Default::default(), + children: Vec::new(), + }, + ), + block: swc_css_ast::SimpleBlock { + span: Default::default(), + name: swc_css_ast::TokenAndSpan { + span: DUMMY_SP, + token: swc_css_ast::Token::LBrace, + }, + value: declaration_list, + }, + } + .into(), + )], + } + } + _ => return None, + } + } + CssMinificationMode::MediaQueryList => { + match swc_css_parser::parse_file::( + &fm, + None, + options.parser, + &mut errors, + ) { + Ok(media_query_list) => swc_css_ast::Stylesheet { + span: Default::default(), + rules: vec![swc_css_ast::Rule::AtRule( + swc_css_ast::AtRule { + span: Default::default(), + name: swc_css_ast::AtRuleName::Ident(swc_css_ast::Ident { + span: Default::default(), + value: "media".into(), + raw: None, + }), + prelude: Some( + swc_css_ast::AtRulePrelude::MediaPrelude( + media_query_list, + ) + .into(), + ), + block: Some(swc_css_ast::SimpleBlock { + span: Default::default(), + name: swc_css_ast::TokenAndSpan { + span: DUMMY_SP, + token: swc_css_ast::Token::LBrace, + }, + // TODO make the `compress_empty` option for CSS + // minifier and + // remove it + value: vec![swc_css_ast::ComponentValue::Str( + Box::new(swc_css_ast::Str { + span: Default::default(), + value: "placeholder".into(), + raw: None, + }), + )], + }), + } + .into(), + )], + }, + _ => return None, + } + } + }; + + // Avoid compress potential invalid CSS + if !errors.is_empty() { + return None; + } + + swc_css_minifier::minify(&mut stylesheet, options.minifier); + + let mut minified = String::new(); + let wr = swc_css_codegen::writer::basic::BasicCssWriter::new( + &mut minified, + None, + swc_css_codegen::writer::basic::BasicCssWriterConfig::default(), + ); + + options.codegen.minify = true; + + let mut gen = swc_css_codegen::CodeGenerator::new(wr, options.codegen); + + match mode { + CssMinificationMode::Stylesheet => { + swc_css_codegen::Emit::emit(&mut gen, &stylesheet).unwrap(); + } + CssMinificationMode::ListOfDeclarations => { + let swc_css_ast::Stylesheet { rules, .. } = &stylesheet; + + // Because CSS is grammar free, protect for fails + let Some(swc_css_ast::Rule::QualifiedRule(qualified_rule)) = rules.first() + else { + return None; + }; + + let swc_css_ast::QualifiedRule { block, .. } = &**qualified_rule; + + swc_css_codegen::Emit::emit(&mut gen, &block).unwrap(); + + minified = minified[1..minified.len() - 1].to_string(); + } + CssMinificationMode::MediaQueryList => { + let swc_css_ast::Stylesheet { rules, .. } = &stylesheet; + + // Because CSS is grammar free, protect for fails + let Some(swc_css_ast::Rule::AtRule(at_rule)) = rules.first() else { + return None; + }; + + let swc_css_ast::AtRule { prelude, .. } = &**at_rule; + + swc_css_codegen::Emit::emit(&mut gen, &prelude).unwrap(); + + minified = minified.trim().to_string(); + } + } + + Some(minified) + } + } + } +} + fn minify_inner( code: &str, opts: MinifyOptions, @@ -402,13 +638,14 @@ fn minify_inner( match document_or_document_fragment { DocumentOrDocumentFragment::Document(ref mut document) => { - minify_document(document, &options); + minify_document_with_custom_css_minifier(document, &options, &CssMinifier); } DocumentOrDocumentFragment::DocumentFragment(ref mut document_fragment) => { - minify_document_fragment( + minify_document_fragment_with_custom_css_minifier( document_fragment, context_element.as_ref().unwrap(), &options, + &CssMinifier, ); } } diff --git a/packages/html/index.ts b/packages/html/index.ts index 7161fba082a9..fcf99a46df3f 100644 --- a/packages/html/index.ts +++ b/packages/html/index.ts @@ -25,7 +25,10 @@ export type Options = { minifyJson?: boolean | { pretty?: boolean }; // TODO improve me after typing `@swc/css` minifyJs?: boolean | { parser?: any; minifier?: any; codegen?: any }; - minifyCss?: boolean | { parser?: any; minifier?: any; codegen?: any }; + minifyCss?: + | boolean + | { lib: "lightningcss" } + | { lib: "swc"; parser?: any; minifier?: any; codegen?: any }; minifyAdditionalScriptsContent?: [string, MinifierType][]; minifyAdditionalAttributes?: [string, MinifierType][]; sortSpaceSeparatedAttributeValues?: boolean;