mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 02:06:08 +03:00
feat(es/minifier): Support format.inline_script
(#8252)
This commit is contained in:
parent
5fed604276
commit
f059270348
@ -36,6 +36,9 @@ pub struct Config {
|
||||
|
||||
#[cfg_attr(feature = "serde-impl", serde(default))]
|
||||
pub emit_assert_for_import_attributes: bool,
|
||||
|
||||
#[cfg_attr(feature = "serde-impl", serde(default))]
|
||||
pub inline_script: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -46,6 +49,7 @@ impl Default for Config {
|
||||
ascii_only: false,
|
||||
omit_last_semi: false,
|
||||
emit_assert_for_import_attributes: false,
|
||||
inline_script: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,4 +82,9 @@ impl Config {
|
||||
self.emit_assert_for_import_attributes = emit_assert_for_import_attributes;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_inline_script(mut self, inline_script: bool) -> Self {
|
||||
self.inline_script = inline_script;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,56 @@ where
|
||||
pub wr: W,
|
||||
}
|
||||
|
||||
fn replace_close_inline_script(raw: &str) -> Cow<str> {
|
||||
let chars = raw.as_bytes();
|
||||
let pattern_len = 8; // </script>
|
||||
let matched = |i: usize| {
|
||||
if i + pattern_len >= chars.len() {
|
||||
return false;
|
||||
}
|
||||
chars[i] == b'<'
|
||||
&& chars[i + 1] == b'/'
|
||||
&& (chars[i + 2] == b's' || chars[i + 2] == b'S')
|
||||
&& (chars[i + 3] == b'c' || chars[i + 3] == b'C')
|
||||
&& (chars[i + 4] == b'r' || chars[i + 4] == b'R')
|
||||
&& (chars[i + 5] == b'i' || chars[i + 5] == b'I')
|
||||
&& (chars[i + 6] == b'p' || chars[i + 6] == b'P')
|
||||
&& (chars[i + 7] == b't' || chars[i + 7] == b'T')
|
||||
&& (chars[i + 8] == b'>'
|
||||
|| chars[i + 8] == b' '
|
||||
|| chars[i + 8] == b'\t'
|
||||
|| chars[i + 8] == b'\n'
|
||||
|| chars[i + 8] == b'\x0C'
|
||||
|| chars[i + 8] == b'\r')
|
||||
};
|
||||
let mut matched_list = (0..chars.len()).filter(|&i| matched(i)).peekable();
|
||||
if matched_list.peek().is_none() {
|
||||
return Cow::Borrowed(raw);
|
||||
}
|
||||
let mut result = String::new();
|
||||
let mut last_end = 0;
|
||||
for start in matched_list {
|
||||
for c in unsafe { chars.get_unchecked(last_end..start) } {
|
||||
result.push(*c as char);
|
||||
}
|
||||
for (index, c) in unsafe { chars.get_unchecked(start..start + pattern_len) }
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
if index == 1 {
|
||||
// "</s" -> <\/s"
|
||||
result.push('\\');
|
||||
}
|
||||
result.push(*c as char);
|
||||
}
|
||||
last_end = start + pattern_len;
|
||||
}
|
||||
for c in unsafe { chars.get_unchecked(last_end..chars.len()) } {
|
||||
result.push(*c as char);
|
||||
}
|
||||
Cow::Owned(result)
|
||||
}
|
||||
|
||||
impl<'a, W, S: SourceMapper> Emitter<'a, W, S>
|
||||
where
|
||||
W: WriteJs,
|
||||
@ -579,7 +629,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let value = get_quoted_utf16(&node.value, self.cfg.ascii_only, target);
|
||||
let mut value = get_quoted_utf16(&node.value, self.cfg.ascii_only, target);
|
||||
|
||||
if self.cfg.inline_script {
|
||||
value = replace_close_inline_script(&value)
|
||||
.replace("\x3c!--", "\\x3c!--")
|
||||
.replace("/--\x3e/", "--\\x3e");
|
||||
}
|
||||
|
||||
self.wr.write_str_lit(DUMMY_SP, &value)?;
|
||||
|
||||
|
@ -130,8 +130,7 @@ pub struct JsMinifyFormatOptions {
|
||||
#[serde(default, alias = "indent_start")]
|
||||
pub indent_start: bool,
|
||||
|
||||
/// Not implemented yet.
|
||||
#[serde(default, alias = "inline_script")]
|
||||
#[serde(default = "true_by_default", alias = "inline_script")]
|
||||
pub inline_script: bool,
|
||||
|
||||
/// Not implemented yet.
|
||||
|
84
crates/swc_ecma_minifier/tests/format.rs
Normal file
84
crates/swc_ecma_minifier/tests/format.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use swc_common::{sync::Lrc, FileName, Mark, SourceMap};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::{
|
||||
text_writer::{omit_trailing_semi, JsWriter, WriteJs},
|
||||
Config, Emitter,
|
||||
};
|
||||
use swc_ecma_minifier::{optimize, option::ExtraOptions};
|
||||
use swc_ecma_parser::{parse_file_as_module, Syntax};
|
||||
use testing::NormalizedOutput;
|
||||
|
||||
fn print(cm: Lrc<SourceMap>, m: &Module, config: Config) -> String {
|
||||
let mut buf = vec![];
|
||||
|
||||
{
|
||||
let mut wr = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)) as Box<dyn WriteJs>;
|
||||
|
||||
if config.minify {
|
||||
wr = Box::new(omit_trailing_semi(wr));
|
||||
}
|
||||
|
||||
let mut emitter = Emitter {
|
||||
cfg: config,
|
||||
cm,
|
||||
comments: None,
|
||||
wr,
|
||||
};
|
||||
|
||||
emitter.emit_module(m).unwrap();
|
||||
}
|
||||
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
||||
|
||||
fn assert_format(src: &str, expected: &str, opts: Config) {
|
||||
testing::run_test2(false, |cm, _| {
|
||||
let fm = cm.new_source_file(FileName::Anon, src.into());
|
||||
|
||||
let program = parse_file_as_module(
|
||||
&fm,
|
||||
Syntax::Es(Default::default()),
|
||||
Default::default(),
|
||||
None,
|
||||
&mut vec![],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let unresolved_mark = Mark::new();
|
||||
let top_level_mark = Mark::new();
|
||||
|
||||
let m = optimize(
|
||||
program.into(),
|
||||
cm.clone(),
|
||||
None,
|
||||
None,
|
||||
&Default::default(),
|
||||
&ExtraOptions {
|
||||
unresolved_mark,
|
||||
top_level_mark,
|
||||
},
|
||||
)
|
||||
.expect_module();
|
||||
|
||||
let actual = print(cm, &m, opts);
|
||||
assert_eq!(
|
||||
NormalizedOutput::from(actual),
|
||||
NormalizedOutput::from(expected.to_owned())
|
||||
);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_script() {
|
||||
let src = r#"
|
||||
console.log("</sCrIpT>");
|
||||
"#;
|
||||
let expected = r#"console.log("<\/sCrIpT>");"#;
|
||||
assert_format(
|
||||
src,
|
||||
expected,
|
||||
Config::default().with_inline_script(true).with_minify(true),
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user