mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 10:12:42 +03:00
feat(html/codegen): Add option for tag omission of self closing void elements (#4971)
This commit is contained in:
parent
b46878625c
commit
d07ab2cb91
@ -26,6 +26,10 @@ pub struct CodegenConfig {
|
||||
pub scripting_enabled: bool,
|
||||
/// Should be used only for `DocumentFragment` code generation
|
||||
pub context_element: Option<Element>,
|
||||
/// By default `true` when `minify` enabled, otherwise `false`
|
||||
pub tag_omission: Option<bool>,
|
||||
/// By default `false` when `minify` enabled, otherwise `true`
|
||||
pub self_closing_void_elements: Option<bool>,
|
||||
}
|
||||
|
||||
enum TagOmissionParent<'a> {
|
||||
@ -44,6 +48,8 @@ where
|
||||
ctx: Ctx,
|
||||
// For legacy `<plaintext>`
|
||||
is_plaintext: bool,
|
||||
tag_omission: bool,
|
||||
self_closing_void_elements: bool,
|
||||
}
|
||||
|
||||
impl<W> CodeGenerator<W>
|
||||
@ -51,17 +57,22 @@ where
|
||||
W: HtmlWriter,
|
||||
{
|
||||
pub fn new(wr: W, config: CodegenConfig) -> Self {
|
||||
let tag_omission = config.tag_omission.unwrap_or(config.minify);
|
||||
let self_closing_void_elements = config.tag_omission.unwrap_or(!config.minify);
|
||||
|
||||
CodeGenerator {
|
||||
wr,
|
||||
config,
|
||||
ctx: Default::default(),
|
||||
is_plaintext: false,
|
||||
tag_omission,
|
||||
self_closing_void_elements,
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
fn emit_document(&mut self, n: &Document) -> Result {
|
||||
if self.config.minify {
|
||||
if self.tag_omission {
|
||||
self.emit_list_for_tag_omission(TagOmissionParent::Document(n))?;
|
||||
} else {
|
||||
self.emit_list(&n.children, ListFormat::NotDelimited)?;
|
||||
@ -76,7 +87,7 @@ where
|
||||
Default::default()
|
||||
};
|
||||
|
||||
if self.config.minify {
|
||||
if self.tag_omission {
|
||||
self.with_ctx(ctx)
|
||||
.emit_list_for_tag_omission(TagOmissionParent::DocumentFragment(n))?;
|
||||
} else {
|
||||
@ -174,7 +185,7 @@ where
|
||||
}
|
||||
|
||||
let has_attributes = !n.attributes.is_empty();
|
||||
let can_omit_start_tag = self.config.minify
|
||||
let can_omit_start_tag = self.tag_omission
|
||||
&& !has_attributes
|
||||
&& n.namespace == Namespace::HTML
|
||||
&& match &*n.tag_name {
|
||||
@ -327,10 +338,10 @@ where
|
||||
}
|
||||
|
||||
if (matches!(n.namespace, Namespace::SVG | Namespace::MATHML) && is_void_element)
|
||||
|| (!self.config.minify
|
||||
&& matches!(n.namespace, Namespace::HTML)
|
||||
|| (self.self_closing_void_elements
|
||||
&& n.is_self_closing
|
||||
&& is_void_element)
|
||||
&& is_void_element
|
||||
&& matches!(n.namespace, Namespace::HTML))
|
||||
{
|
||||
if self.config.minify {
|
||||
let need_space = match n.attributes.last() {
|
||||
@ -387,7 +398,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.minify {
|
||||
if self.tag_omission {
|
||||
self.with_ctx(ctx)
|
||||
.emit_list_for_tag_omission(TagOmissionParent::Element(n))?;
|
||||
} else {
|
||||
@ -397,7 +408,7 @@ where
|
||||
}
|
||||
|
||||
let can_omit_end_tag = self.is_plaintext
|
||||
|| (self.config.minify
|
||||
|| (self.tag_omission
|
||||
&& n.namespace == Namespace::HTML
|
||||
&& match &*n.tag_name {
|
||||
// Tag omission in text/html:
|
||||
|
@ -391,6 +391,37 @@ fn test_document_fragment(input: PathBuf) {
|
||||
);
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/options/self_closing_void_elements/true/**/input.html")]
|
||||
fn test_self_closing_void_elements_true(input: PathBuf) {
|
||||
print_document(
|
||||
&input,
|
||||
None,
|
||||
None,
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: false,
|
||||
tag_omission: Some(false),
|
||||
self_closing_void_elements: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/options/self_closing_void_elements/false/**/input.html")]
|
||||
fn test_self_closing_void_elements_false(input: PathBuf) {
|
||||
print_document(
|
||||
&input,
|
||||
None,
|
||||
None,
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: false,
|
||||
self_closing_void_elements: Some(false),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/options/indent_type/**/input.html")]
|
||||
fn test_indent_type_option(input: PathBuf) {
|
||||
print_document(
|
||||
@ -415,6 +446,19 @@ fn parser_verify(input: PathBuf) {
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: true,
|
||||
tag_omission: Some(false),
|
||||
..Default::default()
|
||||
}),
|
||||
false,
|
||||
);
|
||||
verify_document(
|
||||
&input,
|
||||
None,
|
||||
None,
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: true,
|
||||
tag_omission: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
false,
|
||||
@ -440,6 +484,19 @@ fn parser_recovery_verify(input: PathBuf) {
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: true,
|
||||
tag_omission: Some(false),
|
||||
..Default::default()
|
||||
}),
|
||||
true,
|
||||
);
|
||||
verify_document(
|
||||
&input,
|
||||
None,
|
||||
None,
|
||||
Some(CodegenConfig {
|
||||
scripting_enabled: false,
|
||||
minify: true,
|
||||
tag_omission: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
true,
|
||||
@ -522,6 +579,13 @@ fn html5lib_tests_verify(input: PathBuf) {
|
||||
};
|
||||
let minified_codegen_config = CodegenConfig {
|
||||
minify: true,
|
||||
tag_omission: Some(true),
|
||||
scripting_enabled,
|
||||
..Default::default()
|
||||
};
|
||||
let minified_codegen_config_no_tag_omission = CodegenConfig {
|
||||
minify: true,
|
||||
tag_omission: Some(false),
|
||||
scripting_enabled,
|
||||
..Default::default()
|
||||
};
|
||||
@ -575,12 +639,20 @@ fn html5lib_tests_verify(input: PathBuf) {
|
||||
);
|
||||
verify_document_fragment(
|
||||
&input,
|
||||
context_element,
|
||||
context_element.clone(),
|
||||
Some(parser_config),
|
||||
None,
|
||||
Some(minified_codegen_config),
|
||||
true,
|
||||
);
|
||||
verify_document_fragment(
|
||||
&input,
|
||||
context_element,
|
||||
Some(parser_config),
|
||||
None,
|
||||
Some(minified_codegen_config_no_tag_omission),
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
verify_document(
|
||||
&input,
|
||||
@ -590,6 +662,14 @@ fn html5lib_tests_verify(input: PathBuf) {
|
||||
true,
|
||||
);
|
||||
|
||||
verify_document(
|
||||
&input,
|
||||
Some(parser_config),
|
||||
None,
|
||||
Some(minified_codegen_config_no_tag_omission),
|
||||
true,
|
||||
);
|
||||
|
||||
let relative_path = input.to_string_lossy().replace('-', "_").replace('\\', "/");
|
||||
|
||||
if !IGNORE_TAG_OMISSION
|
||||
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<h1>My First Heading</h1>
|
||||
|
||||
<p>My first paragraph.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,14 @@
|
||||
<link rel="stylesheet" href="https://www.google.com/css/maia.css"
|
||||
type="text/css">
|
||||
|
||||
|
||||
<!-- following are self closing tags --><!-- have no content or child --><html>
|
||||
<head></head><body><br />
|
||||
<hr />
|
||||
<input type="text" />
|
||||
<img src="#URL" alt="image" />
|
||||
<area shape="rect" coords="0,0,100,100" href="#URL" />
|
||||
<!-- SVG and MathML namespace should always have `/` at the end -->
|
||||
<svg><test/></svg>
|
||||
<math><test/></math>
|
||||
</body></html>
|
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head><link rel="stylesheet" href="https://www.google.com/css/maia.css" type="text/css">
|
||||
|
||||
|
||||
<!-- following are self closing tags --><!-- have no content or child -->
|
||||
</head><body><br />
|
||||
<hr />
|
||||
<input type="text" />
|
||||
<img src="#URL" alt="image" />
|
||||
<area shape="rect" coords="0,0,100,100" href="#URL" />
|
||||
<!-- SVG and MathML namespace should always have `/` at the end -->
|
||||
<svg><test /></svg>
|
||||
<math><test /></math>
|
||||
</body></html>
|
@ -0,0 +1,13 @@
|
||||
<link rel="stylesheet" href="https://www.google.com/css/maia.css"
|
||||
type="text/css">
|
||||
|
||||
|
||||
<!-- following are self closing tags --><!-- have no content or child --><html>
|
||||
<head></head><body><br />
|
||||
<hr />
|
||||
<input type="text" />
|
||||
<img src="#URL" alt="image" />
|
||||
<area shape="rect" coords="0,0,100,100" href="#URL" />
|
||||
<svg><test/></svg>
|
||||
<math><test/></math>
|
||||
</body></html>
|
@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head><link rel="stylesheet" href="https://www.google.com/css/maia.css" type="text/css">
|
||||
|
||||
|
||||
<!-- following are self closing tags --><!-- have no content or child -->
|
||||
</head><body><br>
|
||||
<hr>
|
||||
<input type="text">
|
||||
<img src="#URL" alt="image">
|
||||
<area shape="rect" coords="0,0,100,100" href="#URL">
|
||||
<svg><test /></svg>
|
||||
<math><test /></math>
|
||||
</body></html>
|
Loading…
Reference in New Issue
Block a user