diff --git a/.changeset/modern-pillows-look.md b/.changeset/modern-pillows-look.md new file mode 100644 index 000000000..ecde1aa4e --- /dev/null +++ b/.changeset/modern-pillows-look.md @@ -0,0 +1,5 @@ +--- +'@farmfe/plugin-sass': minor +--- + +use grass as the replacement of dart-sass in rust plugin sass diff --git a/Cargo.lock b/Cargo.lock index ea8c2360e..9a02665b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "any_ascii" version = "0.1.7" @@ -538,6 +547,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + [[package]] name = "clipboard-win" version = "4.5.0" @@ -549,6 +573,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1431,7 +1461,7 @@ dependencies = [ "farmfe_testing_helpers", "farmfe_toolkit", "farmfe_toolkit_plugin_types", - "sass-embedded", + "grass", "serde", ] @@ -1898,6 +1928,30 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "grass" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cc4b64880a2264a41f9eab431780e72a68a6c88b9bddef361ba638812d572e" +dependencies = [ + "clap", + "grass_compiler", +] + +[[package]] +name = "grass_compiler" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4feeef87d958eebd4d55431040768b93a5b088202198e0b203adc3c1d468c6" +dependencies = [ + "codemap", + "indexmap 1.9.3", + "lasso", + "once_cell", + "phf", + "rand", +] + [[package]] name = "half" version = "1.8.2" @@ -1913,6 +1967,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1985,9 +2048,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -2135,11 +2198,11 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.3", "libc", "windows-sys 0.48.0", ] @@ -2244,6 +2307,15 @@ dependencies = [ "libc", ] +[[package]] +name = "lasso" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb7b21a526375c5ca55f1a6dfd4e1fad9fa4edd750f530252a718a44b2608f0" +dependencies = [ + "hashbrown 0.11.2", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2380,9 +2452,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b085a4f2cde5781fc4b1717f2e86c62f5cda49de7ba99a7c2eae02b61c9064c" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" @@ -2487,7 +2559,7 @@ dependencies = [ "supports-hyperlinks", "supports-unicode", "terminal_size", - "textwrap", + "textwrap 0.15.2", "thiserror", "unicode-width", ] @@ -2546,12 +2618,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - [[package]] name = "napi" version = "2.12.5" @@ -3075,16 +3141,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -3134,60 +3190,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost", -] - [[package]] name = "psm" version = "0.1.21" @@ -3548,9 +3550,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags 1.3.2", "errno", @@ -3592,26 +3594,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sass-embedded" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91bc40eea43391e185d1760f78b7d254f8f8348da7ff7963e98c10ecd1ed0f15" -dependencies = [ - "atty", - "crossbeam-channel", - "dashmap", - "parking_lot", - "prost", - "prost-build", - "regex", - "rustc-hash", - "serde", - "serde_json", - "url", - "urlencoding", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -4089,6 +4071,12 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + [[package]] name = "subtle" version = "2.4.1" @@ -5045,15 +5033,16 @@ checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -5094,6 +5083,15 @@ dependencies = [ "libc", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "textwrap" version = "0.15.2" @@ -6184,17 +6182,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/rust-plugins/sass/Cargo.toml b/rust-plugins/sass/Cargo.toml index 2366f3d75..227f9bf1c 100755 --- a/rust-plugins/sass/Cargo.toml +++ b/rust-plugins/sass/Cargo.toml @@ -11,8 +11,8 @@ farmfe_core = { version = "*", path = "../../crates/core" } farmfe_macro_plugin = { version = "*", path = "../../crates/macro_plugin" } farmfe_toolkit_plugin_types = { version = "*", path = "../../crates/toolkit_plugin_types" } farmfe_toolkit = { path = "../../crates/toolkit" } -sass-embedded = { version = "0.6.2", features = ["legacy", "serde"] } serde = { version = "1.0", features = ["derive"] } +grass = { version = "0.12.4"} [dev-dependencies] farmfe_testing_helpers = { path = "../../crates/testing_helpers" } diff --git a/rust-plugins/sass/src/lib.rs b/rust-plugins/sass/src/lib.rs index 57520b1ad..48b984620 100755 --- a/rust-plugins/sass/src/lib.rs +++ b/rust-plugins/sass/src/lib.rs @@ -1,45 +1,60 @@ #![deny(clippy::all)] -use farmfe_core::{ - config::Config, - context::CompilationContext, - hashbrown::HashMap, - module::{ModuleId, ModuleType}, - parking_lot::RwLock, - plugin::{Plugin, PluginHookContext, PluginResolveHookParam, ResolveKind}, - relative_path::RelativePath, - serde_json::{self, Value}, -}; -use farmfe_macro_plugin::farm_plugin; -use farmfe_toolkit::{fs, regex::Regex, resolve::follow_symlinks}; -use sass_embedded::{FileImporter, OutputStyle, Sass, StringOptions, StringOptionsBuilder, Url}; -use std::path::PathBuf; -use std::sync::Arc; -use std::{env, fmt::Debug}; -use std::{ - env::consts::{ARCH, OS}, - fmt::Formatter, -}; +use std::path::Path; -const PKG_NAME: &str = "@farmfe/plugin-sass"; +use farmfe_core::{config::Config, module::ModuleType, plugin::Plugin, serde_json}; +use farmfe_macro_plugin::farm_plugin; +use farmfe_toolkit::{fs, regex::Regex}; +use grass; +use serde_json::Value; #[farm_plugin] pub struct FarmPluginSass { - sass_options: String, + options: String, + root: String, regex: Regex, } impl FarmPluginSass { - pub fn new(_config: &Config, options: String) -> Self { + pub fn new(config: &Config, options: String) -> Self { Self { - sass_options: options, + options: options, + root: config.root.clone(), regex: Regex::new(r#"\.(sass|scss)$"#).unwrap(), } } - pub fn get_sass_options(&self, resolve_path: String) -> (StringOptions, HashMap) { - let options = serde_json::from_str(&self.sass_options).unwrap_or_default(); - get_options(options, resolve_path) + pub fn get_sass_options(&self) -> grass::Options { + let options: Value = serde_json::from_str(&self.options).unwrap_or_default(); + let mut sass_options = grass::Options::default(); + + if let Value::Bool(quiet) = options.get("quiet").unwrap_or(&Value::Null) { + sass_options = sass_options.quiet(*quiet); + } + + if let Value::Bool(allows_charset) = options.get("allows_charset").unwrap_or(&Value::Null) { + sass_options = sass_options.allows_charset(*allows_charset); + } + + if let Value::Bool(unicode_error_messages) = options + .get("unicode_error_messages") + .unwrap_or(&Value::Null) + { + sass_options = sass_options.unicode_error_messages(*unicode_error_messages); + } + + let mut paths = vec![Path::new(&self.root)]; + + if let Value::Array(load_paths) = options.get("load_paths").unwrap_or(&Value::Null) { + for path in load_paths { + if let Value::String(path) = path { + paths.push(Path::new(path)); + } + } + } + + sass_options = sass_options.load_paths(&paths); + sass_options } } @@ -135,170 +150,19 @@ impl Plugin for FarmPluginSass { context: &std::sync::Arc, ) -> farmfe_core::error::Result> { if param.module_type == ModuleType::Custom(String::from("sass")) { - let exe_path: PathBuf = get_exe_path(); - let mut sass = Sass::new(exe_path).unwrap_or_else(|e| { - panic!( - "\n sass-embedded init error: {},\n Please try to install manually. eg: \n pnpm install sass-embedded-{}-{} \n", - e.message(), - get_os(), - get_arch() - ) - }); - - let (mut string_options, additional_options) = - self.get_sass_options(param.resolved_path.to_string()); - - let paths = Arc::new(RwLock::new(vec![])); - let cloned_context = context.clone(); - - let import_collection = Box::new(FileImporterCollection { - paths: paths.clone(), - importer: param.module_id.clone().into(), - context: cloned_context, - }); - // TODO support source map for additionalData - let content = if let Some(additional_data) = additional_options.get("additionalData") { - format!("{}\n{}", additional_data, param.content) - } else { - param.content.clone() - }; - - string_options - .common - .importers - .push(sass_embedded::SassImporter::FileImporter(import_collection)); - string_options.url = None; - - let compile_result = sass.compile_string(&content, string_options).map_err(|e| { + let sass_options = self.get_sass_options(); + let css = grass::from_string(¶m.content.to_owned(), &sass_options).map_err(|e| { farmfe_core::error::CompilationError::TransformError { resolved_path: param.resolved_path.to_string(), - msg: e.message().to_string(), + msg: e.to_string(), } })?; - - let paths = paths.read(); - - context - .add_watch_files(param.resolved_path.to_string(), paths.iter().collect()) - .expect("cannot add file to watch graph"); - return Ok(Some(farmfe_core::plugin::PluginTransformHookResult { - content: compile_result.css, - source_map: compile_result.source_map, + content: css, + source_map: None, module_type: Some(farmfe_core::module::ModuleType::Css), })); } Ok(None) } } - -fn get_os() -> &'static str { - match OS { - "linux" => "linux", - "macos" => "darwin", - "windows" => "win32", - os => panic!("dart-sass-embed is not supported OS: {}", os), - } -} - -fn get_arch() -> &'static str { - match ARCH { - "x86" => "ia32", - "x86_64" => "x64", - "aarch64" => "arm64", - arch => panic!("dart-sass-embed is not supported arch: {}", arch), - } -} - -fn get_exe_path() -> PathBuf { - let os = get_os(); - let arch = get_arch(); - let entry_file = if let "win32" = os { - "dart-sass-embedded.bat" - } else { - "dart-sass-embedded" - }; - - let cur_dir = env::current_dir().unwrap(); - - // user manually installs related dependencies - let manual_installation_path = RelativePath::new(&format!( - "node_modules/sass-embedded-{os}-{arch}/dart-sass-embedded/{entry_file}" - )) - .to_logical_path(&cur_dir); - - let pkg_dir = follow_symlinks( - RelativePath::new("node_modules") - .join(PKG_NAME) - .to_logical_path(&cur_dir), - ); - - // find closest node_modules start from pkg_dir - let mut cur_dir = pkg_dir; - - while !cur_dir.join("node_modules").exists() { - if cur_dir.parent().is_none() { - panic!("can not find node_modules in @farmfe/plugin-sass"); - } - - cur_dir = cur_dir.parent().unwrap().to_path_buf(); - } - - let default_path = RelativePath::new("node_modules") - .join(format!("sass-embedded-{os}-{arch}")) - .join(format!("dart-sass-embedded/{entry_file}")) - .to_logical_path(&cur_dir); - - if manual_installation_path.exists() { - manual_installation_path - } else { - default_path - } -} - -fn get_options(options: Value, resolve_path: String) -> (StringOptions, HashMap) { - let mut builder = StringOptionsBuilder::new(); - builder = builder.url(Url::from_file_path(resolve_path).unwrap()); - if let Some(source_map) = options.get("sourceMap") { - builder = builder.source_map(source_map.as_bool().unwrap()); - } - if let Some(source_map_include_sources) = options.get("sourceMapIncludeSources") { - builder = builder.source_map(source_map_include_sources.as_bool().unwrap()); - } - if let Some(alert_ascii) = options.get("alertAscii") { - builder = builder.alert_ascii(alert_ascii.as_bool().unwrap()); - } - if let Some(alert_color) = options.get("alertColor") { - builder = builder.alert_color(alert_color.as_bool().unwrap()) - } - if let Some(charset) = options.get("charset") { - builder = builder.charset(charset.as_bool().unwrap()); - } - if let Some(quiet_deps) = options.get("quietDeps") { - builder = builder.quiet_deps(quiet_deps.as_bool().unwrap()); - } - if let Some(verbose) = options.get("verbose") { - builder = builder.verbose(verbose.as_bool().unwrap()); - } - if let Some(style) = options.get("style") { - let output_style = match style.as_str().unwrap() { - "expanded" => OutputStyle::Expanded, - "compressed" => OutputStyle::Compressed, - _ => panic!("sass stringOptions does not support this style configuration"), - }; - builder = builder.style(output_style); - } - - let mut additional_data = HashMap::new(); - - if let Some(additional_date) = options.get("additionalData") { - additional_data.insert( - "additionalData".to_string(), - additional_date.as_str().unwrap().to_string(), - ); - } - - // TODO support more options - - (builder.build(), additional_data) -} diff --git a/rust-plugins/sass/tests/mod.rs b/rust-plugins/sass/tests/mod.rs index 6097a7d61..01881d192 100755 --- a/rust-plugins/sass/tests/mod.rs +++ b/rust-plugins/sass/tests/mod.rs @@ -46,7 +46,7 @@ fn test() { .unwrap() .unwrap(); let expected = - "body {\n color: #000;\n}\nbody .description:hover {\n background-color: #f8f9fa;\n}"; + "body {\n color: #000;\n}\nbody .description:hover {\n background-color: #f8f9fa;\n}\n"; assert_eq!(transformed.content, expected); }); }