use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use lazy_static::lazy_static; #[cfg(feature = "js-esbuild")] use { crate::proc::checkpoint::WriteCheckpoint, crate::proc::EsbuildSection, esbuild_rs::{TransformOptions, TransformOptionsBuilder}, std::sync::Arc, }; use crate::cfg::Cfg; use crate::err::ProcessingResult; use crate::proc::MatchAction::*; use crate::proc::MatchMode::*; use crate::proc::Processor; #[cfg(feature = "js-esbuild")] lazy_static! { static ref TRANSFORM_OPTIONS: Arc = { let mut builder = TransformOptionsBuilder::new(); builder.minify_identifiers = true; builder.minify_syntax = true; builder.minify_whitespace = true; builder.build() }; } lazy_static! { static ref SCRIPT_END: AhoCorasick = AhoCorasickBuilder::new().ascii_case_insensitive(true).build(&[" ProcessingResult<()> { #[cfg(feature = "js-esbuild")] let start = WriteCheckpoint::new(proc); proc.require_not_at_end()?; proc.m(WhileNotSeq(&SCRIPT_END), Keep); // `process_tag` will require closing tag. // TODO This is copied from style.rs. #[cfg(feature = "js-esbuild")] if js && cfg.minify_js { let (wg, results) = proc.new_esbuild_section(); let src = start.written_range(proc); unsafe { esbuild_rs::transform_direct_unmanaged(&proc[src], &TRANSFORM_OPTIONS.clone(), move |result| { let mut guard = results.lock().unwrap(); // TODO Handle other forms: // 1 < /script/.exec(a).length // ` ${` ${a // /* // Considerations: // - Need to parse strings (e.g. "", '', ``) so syntax within strings aren't mistakenly interpreted as code. // - Need to be able to parse regex literals to determine string delimiters aren't actually characters in the regex. // - Determining whether a slash is division or regex requires a full-blown JS parser to handle all cases (this is a well-known JS parsing problem). // - `/::new(); // SCRIPT_END must be case insensitive. SCRIPT_END.replace_all_with_bytes( result.code.as_str().trim().as_bytes(), &mut escaped, |_, orig, dst| { dst.extend(b"<\\/"); // Keep original case. dst.extend(&orig[2..]); true }, ); guard.push(EsbuildSection { src, escaped, }); // Drop Arc reference and Mutex guard before marking task as complete as it's possible proc::finish // waiting on WaitGroup will resume before Arc/Mutex is dropped after exiting this function. drop(guard); drop(results); drop(wg); }); }; }; Ok(()) }