use crate::context::LintContext;
use crate::diagnostic::Severity;
use crate::preset::LintPreset;
use vize_relief::ast::{DirectiveNode, ElementNode, ForNode, IfNode, InterpolationNode, RootNode};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleCategory {
Essential,
StronglyRecommended,
Recommended,
Vapor,
Musea,
Accessibility,
HtmlConformance,
TypeAware,
}
pub struct RuleMeta {
pub name: &'static str,
pub description: &'static str,
pub category: RuleCategory,
pub fixable: bool,
pub default_severity: Severity,
}
pub trait Rule: Send + Sync {
fn meta(&self) -> &'static RuleMeta;
#[allow(unused_variables)]
fn run_on_sfc<'a>(&self, ctx: &mut LintContext<'a>) {}
#[allow(unused_variables)]
fn run_on_template<'a>(&self, ctx: &mut LintContext<'a>, root: &RootNode<'a>) {}
#[allow(unused_variables)]
fn enter_element<'a>(&self, ctx: &mut LintContext<'a>, element: &ElementNode<'a>) {}
#[allow(unused_variables)]
fn exit_element<'a>(&self, ctx: &mut LintContext<'a>, element: &ElementNode<'a>) {}
#[allow(unused_variables)]
fn check_directive<'a>(
&self,
ctx: &mut LintContext<'a>,
element: &ElementNode<'a>,
directive: &DirectiveNode<'a>,
) {
}
#[allow(unused_variables)]
fn check_for<'a>(&self, ctx: &mut LintContext<'a>, for_node: &ForNode<'a>) {}
#[allow(unused_variables)]
fn check_if<'a>(&self, ctx: &mut LintContext<'a>, if_node: &IfNode<'a>) {}
#[allow(unused_variables)]
fn check_interpolation<'a>(
&self,
ctx: &mut LintContext<'a>,
interpolation: &InterpolationNode<'a>,
) {
}
}
pub struct RuleRegistry {
rules: Vec<Box<dyn Rule>>,
}
impl RuleRegistry {
const ESSENTIAL_CAPACITY: usize = 32;
const HAPPY_PATH_CAPACITY: usize = 90;
pub fn new() -> Self {
Self { rules: Vec::new() }
}
#[inline]
fn with_capacity(capacity: usize) -> Self {
Self {
rules: Vec::with_capacity(capacity),
}
}
pub fn register(&mut self, rule: Box<dyn Rule>) {
self.rules.push(rule);
}
pub fn add(&mut self, rule: Box<dyn Rule>) {
self.register(rule);
}
pub fn rules(&self) -> &[Box<dyn Rule>] {
&self.rules
}
pub fn has_rule(&self, name: &str) -> bool {
self.rules.iter().any(|rule| rule.meta().name == name)
}
pub fn with_preset(preset: LintPreset) -> Self {
match preset {
LintPreset::HappyPath => Self::with_happy_path(),
LintPreset::Opinionated => Self::with_opinionated(),
LintPreset::Essential => Self::with_essential(),
LintPreset::Nuxt => Self::with_nuxt(),
}
}
pub fn with_happy_path() -> Self {
let mut registry = Self::with_capacity(Self::HAPPY_PATH_CAPACITY);
registry.register(Box::new(crate::rules::vue::RequireVForKey));
registry.register(Box::new(crate::rules::vue::ValidVFor));
registry.register(Box::new(crate::rules::vue::NoUseVIfWithVFor));
registry.register(Box::new(crate::rules::vue::NoUnusedVars::default()));
registry.register(Box::new(crate::rules::vue::NoDuplicateAttributes::default()));
registry.register(Box::new(crate::rules::vue::NoTemplateKey));
registry.register(Box::new(crate::rules::vue::NoTextareaMustache));
registry.register(Box::new(crate::rules::vue::ValidVElse));
registry.register(Box::new(crate::rules::vue::ValidVIf));
registry.register(Box::new(crate::rules::vue::ValidVOn));
registry.register(Box::new(crate::rules::vue::ValidVBind));
registry.register(Box::new(crate::rules::vue::ValidVModel));
registry.register(Box::new(crate::rules::vue::ValidVShow));
registry.register(Box::new(crate::rules::vue::NoDupeVElseIf));
registry.register(Box::new(
crate::rules::vue::NoReservedComponentNames::default(),
));
registry.register(Box::new(crate::rules::vue::ComponentDefinitionNameCasing));
registry.register(Box::new(crate::rules::vue::HtmlQuotes::default()));
registry.register(Box::new(
crate::rules::vue::MustacheInterpolationSpacing::default(),
));
registry.register(Box::new(crate::rules::vue::NoLoneTemplate));
registry.register(Box::new(crate::rules::vue::NoMultiSpaces::default()));
registry.register(Box::new(crate::rules::vue::PropNameCasing));
registry.register(Box::new(crate::rules::vue::VOnStyle::default()));
registry.register(Box::new(crate::rules::vue::VSlotStyle::default()));
registry.register(Box::new(crate::rules::vue::ValidVSlot));
registry.register(Box::new(crate::rules::vue::NoChildContent));
registry.register(Box::new(crate::rules::vue::ValidAttributeName));
registry.register(Box::new(crate::rules::vue::AttributeHyphenation::default()));
registry.register(Box::new(crate::rules::vue::AttributeOrder));
registry.register(Box::new(crate::rules::vue::NoVTextVHtmlOnComponent));
registry.register(Box::new(crate::rules::vue::RequireComponentIs));
registry.register(Box::new(crate::rules::vue::RequireScopedStyle));
registry.register(Box::new(crate::rules::vue::SfcElementOrder));
registry.register(Box::new(crate::rules::vue::SingleStyleBlock));
registry.register(Box::new(crate::rules::vue::NoUselessTemplateAttributes));
registry.register(Box::new(crate::rules::vue::ValidVMemo));
registry.register(Box::new(crate::rules::vapor::NoVueLifecycleEvents));
registry.register(Box::new(crate::rules::vue::NoVHtml));
registry.register(Box::new(crate::rules::vue::NoUnsafeUrl));
registry.register(Box::new(crate::rules::a11y::ImgAlt));
registry.register(Box::new(crate::rules::a11y::AnchorHasContent));
registry.register(Box::new(crate::rules::a11y::HeadingHasContent));
registry.register(Box::new(crate::rules::a11y::IframeHasTitle));
registry.register(Box::new(crate::rules::a11y::NoDistractingElements));
registry.register(Box::new(crate::rules::a11y::NoIForIcon));
registry.register(Box::new(crate::rules::a11y::TabindexNoPositive));
registry.register(Box::new(crate::rules::a11y::ClickEventsHaveKeyEvents));
registry.register(Box::new(crate::rules::a11y::FormControlHasLabel));
registry.register(Box::new(crate::rules::a11y::AriaProps));
registry.register(Box::new(crate::rules::a11y::AriaRole::default()));
registry.register(Box::new(crate::rules::a11y::NoAriaHiddenOnFocusable));
registry.register(Box::new(crate::rules::a11y::NoAccessKey));
registry.register(Box::new(crate::rules::a11y::NoAutofocus));
registry.register(Box::new(crate::rules::a11y::NoRolePresentationOnFocusable));
registry.register(Box::new(crate::rules::a11y::AriaUnsupportedElements));
registry.register(Box::new(crate::rules::a11y::NoRedundantRoles));
registry.register(Box::new(crate::rules::a11y::MouseEventsHaveKeyEvents));
registry.register(Box::new(crate::rules::a11y::AltText));
registry.register(Box::new(crate::rules::a11y::AnchorIsValid));
registry.register(Box::new(crate::rules::a11y::LabelHasFor));
registry.register(Box::new(crate::rules::a11y::InteractiveSupportsFocus));
registry.register(Box::new(crate::rules::a11y::RoleHasRequiredAriaProps));
registry.register(Box::new(crate::rules::a11y::MediaHasCaption));
registry.register(Box::new(crate::rules::a11y::NoStaticElementInteractions));
registry.register(Box::new(crate::rules::a11y::NoReferToNonExistentId));
registry.register(Box::new(crate::rules::vue::PermittedContents));
registry.register(Box::new(crate::rules::html::DeprecatedElement));
registry.register(Box::new(crate::rules::html::DeprecatedAttr));
registry.register(Box::new(crate::rules::html::NoConsecutiveBr));
registry.register(Box::new(crate::rules::html::IdDuplication));
registry.register(Box::new(crate::rules::html::NoDuplicateDt));
registry.register(Box::new(crate::rules::html::NoEmptyPalpableContent));
registry.register(Box::new(crate::rules::html::RequireDatetime));
registry.register(Box::new(crate::rules::ssr::NoBrowserGlobalsInSsr));
registry.register(Box::new(crate::rules::ssr::NoHydrationMismatch));
registry.register(Box::new(crate::rules::vue::NoUnusedComponents::default()));
registry.register(Box::new(crate::rules::vue::NoMutatingProps));
registry.register(Box::new(crate::rules::vue::NoUnusedProperties::default()));
#[cfg(not(target_arch = "wasm32"))]
registry.register(Box::new(
crate::rules::type_aware::RequireTypedProps::default(),
));
#[cfg(not(target_arch = "wasm32"))]
registry.register(Box::new(
crate::rules::type_aware::RequireTypedEmits::default(),
));
registry
}
pub fn with_recommended() -> Self {
Self::with_happy_path()
}
pub fn with_essential() -> Self {
let mut registry = Self::with_capacity(Self::ESSENTIAL_CAPACITY);
registry.register(Box::new(crate::rules::vue::RequireVForKey));
registry.register(Box::new(crate::rules::vue::ValidVFor));
registry.register(Box::new(crate::rules::vue::NoUseVIfWithVFor));
registry.register(Box::new(crate::rules::vue::NoUnusedVars::default()));
registry.register(Box::new(crate::rules::vue::NoDuplicateAttributes::default()));
registry.register(Box::new(crate::rules::vue::NoTemplateKey));
registry.register(Box::new(crate::rules::vue::NoTextareaMustache));
registry.register(Box::new(crate::rules::vue::ValidVElse));
registry.register(Box::new(crate::rules::vue::ValidVIf));
registry.register(Box::new(crate::rules::vue::ValidVOn));
registry.register(Box::new(crate::rules::vue::ValidVBind));
registry.register(Box::new(crate::rules::vue::ValidVModel));
registry.register(Box::new(crate::rules::vue::ValidVShow));
registry.register(Box::new(crate::rules::vue::NoDupeVElseIf));
registry.register(Box::new(
crate::rules::vue::NoReservedComponentNames::default(),
));
registry.register(Box::new(crate::rules::vue::ValidVSlot));
registry.register(Box::new(
crate::rules::vue::MultiWordComponentNames::default(),
));
registry.register(Box::new(crate::rules::vue::NoChildContent));
registry.register(Box::new(crate::rules::vue::ValidAttributeName));
registry.register(Box::new(crate::rules::vue::NoVTextVHtmlOnComponent));
registry.register(Box::new(crate::rules::vue::RequireComponentIs));
registry.register(Box::new(crate::rules::vue::NoUselessTemplateAttributes));
registry.register(Box::new(crate::rules::vue::ValidVMemo));
registry.register(Box::new(crate::rules::vue::UseVOnExact));
registry.register(Box::new(crate::rules::vue::NoVHtml));
registry.register(Box::new(crate::rules::vue::NoUnsafeUrl));
registry.register(Box::new(crate::rules::html::IdDuplication));
registry
}
pub fn with_opinionated() -> Self {
let mut registry = Self::with_happy_path();
crate::rules::opinionated::register(&mut registry);
registry
}
pub fn with_all() -> Self {
Self::with_opinionated()
}
pub fn with_nuxt() -> Self {
let mut registry = Self::with_happy_path();
crate::rules::opinionated::register_nuxt(&mut registry);
registry
}
}
impl Default for RuleRegistry {
fn default() -> Self {
Self::with_preset(LintPreset::default())
}
}