use super::{JsxFile, JsxFileError, JsxFileProducer, RejectedJsxFile};
use crate::artifact::ArtifactError;
use crate::file::FileArtifact;
use alloc::{boxed::Box, vec::Vec};
use vize_l0::{Span, profile};
mod emit;
mod prepare;
mod whitespace;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsxMarkupIssueKind {
File(JsxFileError),
Artifact(ArtifactError),
InvalidRecord,
RootExpression,
Fragment,
Component,
Namespace,
SpreadAttribute,
SpreadChild,
DynamicAttribute,
DynamicChild,
SpecialAttribute,
ValueNormalization,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct JsxMarkupIssue {
pub kind: JsxMarkupIssueKind,
pub span: Span,
}
pub struct JsxMarkupFile<'a> {
owner: JsxFile<'a>,
}
impl<'a> JsxMarkupFile<'a> {
#[must_use]
pub fn owner(&self) -> &JsxFile<'a> {
&self.owner
}
#[must_use]
pub fn file(&self) -> &FileArtifact<'a> {
self.owner.file()
}
pub fn fragment_boundaries(&self) -> impl Iterator<Item = super::JsxNode<'_, 'a>> {
self.owner.nodes().filter(|node| {
matches!(
node.kind(),
Some(
crate::resolution::SyntaxKind::FragmentOpening
| crate::resolution::SyntaxKind::FragmentClosing
)
)
})
}
}
pub struct RejectedJsxMarkupFile<'a> {
owner: Result<JsxFile<'a>, Box<RejectedJsxFile<'a>>>,
issues: Vec<JsxMarkupIssue>,
}
impl<'a> RejectedJsxMarkupFile<'a> {
#[must_use]
pub fn owner(&self) -> Option<&JsxFile<'a>> {
self.owner.as_ref().ok()
}
#[must_use]
pub fn rejected_owner(&self) -> Option<&RejectedJsxFile<'a>> {
self.owner.as_ref().err().map(Box::as_ref)
}
#[must_use]
pub fn issues(&self) -> &[JsxMarkupIssue] {
&self.issues
}
}
impl<'a> JsxFileProducer<'a> {
pub fn finish_markup(mut self) -> Result<JsxMarkupFile<'a>, Box<RejectedJsxMarkupFile<'a>>> {
let span = self.block.span();
let (roots, mut issues) = profile!(
"l2.jsx_markup.prepare",
prepare::roots(&self.recorder.records, self.block.root_source())
);
if issues.is_empty() {
profile!("l2.jsx_markup.emit", {
let mut region = self.producer.jsx_region();
for root in roots {
if let Err(error) = emit::node(&self.recorder.records, root, &mut region) {
issues.push(JsxMarkupIssue {
kind: JsxMarkupIssueKind::Artifact(error),
span,
});
break;
}
}
});
}
let owner = profile!("l2.jsx_markup.finish_file", self.finish());
if let Err(rejected) = &owner {
issues.push(JsxMarkupIssue {
kind: JsxMarkupIssueKind::File(rejected.error()),
span,
});
}
match owner {
Ok(owner) if issues.is_empty() => Ok(JsxMarkupFile { owner }),
owner => Err(Box::new(RejectedJsxMarkupFile { owner, issues })),
}
}
}
#[cfg(test)]
mod tests;