89 lines
3.4 KiB
Rust
89 lines
3.4 KiB
Rust
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<TransformOptions> = {
|
|
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(&["</script"]);
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn process_script(proc: &mut Processor, cfg: &Cfg, js: bool) -> 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</script/} `} `
|
|
// // </script>
|
|
// /* </script>
|
|
// 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).
|
|
// - `/</script` or `/</ script` are not valid JS so don't need to be handled.
|
|
let mut escaped = Vec::<u8>::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(())
|
|
}
|