#![allow(clippy::needless_update)] use std::path::PathBuf; use swc_common::{errors::Handler, input::SourceFileInput, Spanned}; use swc_html_ast::*; use swc_html_parser::{ lexer::Lexer, parser::{PResult, Parser, ParserConfig}, }; use swc_html_visit::{Visit, VisitWith}; use testing::NormalizedOutput; fn test_pass(input: PathBuf, config: ParserConfig) { testing::run_test2(false, |cm, handler| { let json_path = input.parent().unwrap().join("output.json"); let fm = cm.load_file(&input).unwrap(); let lexer = Lexer::new(SourceFileInput::from(&*fm), config); let mut parser = Parser::new(lexer, config); let document: PResult = parser.parse_document(); let errors = parser.take_errors(); for err in &errors { err.to_diagnostics(&handler).emit(); } if !errors.is_empty() { return Err(()); } match document { Ok(document) => { let actual_json = serde_json::to_string_pretty(&document) .map(NormalizedOutput::from) .expect("failed to serialize document"); actual_json.compare_to_file(&json_path).unwrap(); Ok(()) } Err(err) => { let mut d = err.to_diagnostics(&handler); d.note(&format!("current token = {}", parser.dump_cur())); d.emit(); Err(()) } } }) .unwrap(); } fn test_recovery(input: PathBuf, config: ParserConfig) { let stderr_path = input.parent().unwrap().join("output.stderr"); let mut recovered = false; let stderr = testing::run_test2(false, |cm, handler| { // Type annotation if false { return Ok(()); } let json_path = input.parent().unwrap().join("output.json"); let fm = cm.load_file(&input).unwrap(); let lexer = Lexer::new(SourceFileInput::from(&*fm), config); let mut parser = Parser::new(lexer, config); let document: PResult = parser.parse_document(); let errors = parser.take_errors(); for err in &errors { err.to_diagnostics(&handler).emit(); } if !errors.is_empty() { recovered = true; } match document { Ok(document) => { let actual_json = serde_json::to_string_pretty(&document) .map(NormalizedOutput::from) .expect("failed to serialize document"); actual_json.compare_to_file(&json_path).unwrap(); Err(()) } Err(err) => { let mut d = err.to_diagnostics(&handler); d.note(&format!("current token = {}", parser.dump_cur())); d.emit(); Err(()) } } }) .unwrap_err(); if !recovered { panic!( "Parser should emit errors (recover mode), but parser parsed everything successfully \ {}", stderr ); } stderr.compare_to_file(&stderr_path).unwrap(); } struct SpanVisualizer<'a> { handler: &'a Handler, } macro_rules! mtd { ($T:ty,$name:ident) => { fn $name(&mut self, n: &$T) { self.handler .struct_span_err(n.span(), stringify!($T)) .emit(); n.visit_children_with(self); } }; } impl Visit for SpanVisualizer<'_> { mtd!(Document, visit_document); mtd!(DocumentFragment, visit_document_fragment); mtd!(Child, visit_child); mtd!(DocumentType, visit_document_type); mtd!(Element, visit_element); mtd!(Attribute, visit_attribute); mtd!(Text, visit_text); mtd!(Comment, visit_comment); fn visit_token_and_span(&mut self, n: &TokenAndSpan) { self.handler .struct_span_err(n.span, &format!("{:?}", n.token)) .emit(); } } fn test_span_visualizer(input: PathBuf, config: ParserConfig) { let dir = input.parent().unwrap().to_path_buf(); let output = testing::run_test2(false, |cm, handler| { // Type annotation if false { return Ok(()); } let fm = cm.load_file(&input).unwrap(); let lexer = Lexer::new(SourceFileInput::from(&*fm), config); let mut parser = Parser::new(lexer, config); let document: PResult = parser.parse_document(); match document { Ok(document) => { document.visit_with(&mut SpanVisualizer { handler: &handler }); Err(()) } Err(err) => { let mut d = err.to_diagnostics(&handler); d.note(&format!("current token = {}", parser.dump_cur())); d.emit(); panic!(); } } }) .unwrap_err(); output .compare_to_file(&dir.join("span.rust-debug")) .unwrap(); } #[testing::fixture("tests/fixture/**/input.html")] fn pass(input: PathBuf) { test_pass( input, ParserConfig { ..Default::default() }, ) } #[testing::fixture("tests/recovery/**/input.html")] fn recovery(input: PathBuf) { test_recovery( input, ParserConfig { ..Default::default() }, ) } #[testing::fixture("tests/fixture/**/input.html")] #[testing::fixture("tests/recovery/**/input.html")] fn span_visualizer(input: PathBuf) { test_span_visualizer( input, ParserConfig { ..Default::default() }, ) }