mod no_async_in_computed;
mod no_deep_destructure_in_props;
mod no_get_current_instance;
mod no_import_compiler_macros;
mod no_internal_imports;
mod no_next_tick;
mod no_options_api;
mod no_reactive_destructure;
mod no_reserved_identifiers;
mod no_top_level_ref_in_script;
mod no_with_defaults;
mod prefer_computed;
mod prefer_import_from_vue;
mod prefer_ref_over_reactive;
mod prefer_use_attrs;
mod prefer_use_id;
mod prefer_use_slots;
mod prefer_use_template_ref;
mod require_function_return_type;
mod require_symbol_provide;
use memchr::memmem;
use crate::diagnostic::{LintDiagnostic, Severity};
use vize_carton::profile;
pub use no_async_in_computed::NoAsyncInComputed;
pub use no_deep_destructure_in_props::NoDeepDestructureInProps;
pub use no_get_current_instance::NoGetCurrentInstance;
pub use no_import_compiler_macros::NoImportCompilerMacros;
pub use no_internal_imports::NoInternalImports;
pub use no_next_tick::NoNextTick;
pub use no_options_api::NoOptionsApi;
pub use no_reactive_destructure::NoReactiveDestructure;
pub use no_reserved_identifiers::NoReservedIdentifiers;
pub use no_top_level_ref_in_script::NoTopLevelRefInScript;
pub use no_with_defaults::NoWithDefaults;
pub use prefer_computed::PreferComputed;
pub use prefer_import_from_vue::PreferImportFromVue;
pub use prefer_ref_over_reactive::PreferRefOverReactive;
pub use prefer_use_attrs::PreferUseAttrs;
pub use prefer_use_id::PreferUseId;
pub use prefer_use_slots::PreferUseSlots;
pub use prefer_use_template_ref::PreferUseTemplateRef;
pub use require_function_return_type::RequireFunctionReturnType;
pub use require_symbol_provide::RequireSymbolProvide;
pub struct ScriptRuleMeta {
pub name: &'static str,
pub description: &'static str,
pub default_severity: Severity,
}
#[derive(Debug, Default)]
pub struct ScriptLintResult {
pub diagnostics: Vec<LintDiagnostic>,
pub error_count: usize,
pub warning_count: usize,
}
impl ScriptLintResult {
pub fn add_diagnostic(&mut self, diagnostic: LintDiagnostic) {
match diagnostic.severity {
Severity::Error => self.error_count += 1,
Severity::Warning => self.warning_count += 1,
}
self.diagnostics.push(diagnostic);
}
pub fn has_errors(&self) -> bool {
self.error_count > 0
}
pub fn has_warnings(&self) -> bool {
self.warning_count > 0
}
}
pub trait ScriptRule: Send + Sync {
fn meta(&self) -> &'static ScriptRuleMeta;
fn check(&self, source: &str, offset: usize, result: &mut ScriptLintResult);
}
pub struct ScriptLinter {
rules: Vec<Box<dyn ScriptRule>>,
}
impl ScriptLinter {
pub fn new() -> Self {
Self { rules: Vec::new() }
}
pub fn with_all_rules() -> Self {
Self {
rules: vec![
Box::new(PreferImportFromVue),
Box::new(NoInternalImports),
Box::new(NoOptionsApi),
Box::new(NoGetCurrentInstance),
Box::new(NoNextTick),
],
}
}
pub fn with_vapor_rules() -> Self {
Self {
rules: vec![
Box::new(NoOptionsApi),
Box::new(NoGetCurrentInstance),
Box::new(NoNextTick),
],
}
}
pub fn add_rule(&mut self, rule: Box<dyn ScriptRule>) {
self.rules.push(rule);
}
pub fn lint(&self, source: &str, offset: usize) -> ScriptLintResult {
let mut result = ScriptLintResult::default();
for rule in &self.rules {
profile!("patina.script_linter.rule.check", {
rule.check(source, offset, &mut result);
});
}
result
}
#[inline]
pub fn has_vue_imports(source: &str) -> bool {
let bytes = source.as_bytes();
memmem::find(bytes, b"from 'vue'").is_some()
|| memmem::find(bytes, b"from \"vue\"").is_some()
|| memmem::find(bytes, b"from '@vue/").is_some()
|| memmem::find(bytes, b"from \"@vue/").is_some()
}
}
impl Default for ScriptLinter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::ScriptLinter;
#[test]
fn test_has_vue_imports() {
assert!(ScriptLinter::has_vue_imports("import { ref } from 'vue'"));
assert!(ScriptLinter::has_vue_imports("import { ref } from \"vue\""));
assert!(ScriptLinter::has_vue_imports(
"import { h } from '@vue/runtime-core'"
));
assert!(!ScriptLinter::has_vue_imports("import { foo } from 'bar'"));
}
#[test]
fn test_empty_linter() {
let linter = ScriptLinter::new();
let result = linter.lint("import { ref } from 'vue'", 0);
assert_eq!(result.error_count, 0);
assert_eq!(result.warning_count, 0);
}
}