mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 08:36:25 +03:00
Upstream sha fa44839f72.
Advisory handling. `severity` becomes the canonical registry field: the
`advisory` bool leaves `Antipattern`, `advisory_rule_ids` filters on
`severity == "advisory"`, and `derive_advisory_flag` stamps the finding's
`advisory: true` from the effective severity, so a per-finding promotion or
demotion carries the flag. The html and browser engines call it after their
severity override; the detect CLI and the hook accept either spelling; the
driver's serializer and the wasm registry exports derive it the same way.
em-dash-overuse moves from `advisory: true` to `severity: "advisory"`.
URL scans. `expand_joined_url_targets` splits an argv value that is entirely
whitespace-separated URLs and leaves paths with spaces alone. The browser
driver reads the readable linked-stylesheet corpus into the HTML pattern
corpora and resolves a finding's selector with `selector_nodes_for_live_dom`
/ `pseudo_element_host_selector`, so an unresolvable selector drops the
finding instead of keeping it page-level. The CSSOM walk itself is page JS:
`browser-bundle/15-snapshot.js` gains `__snapLinkedStylesheetText` (grouping
rules flattened, container-query probes, effective keyframes) and puts it in
the snapshot as `linkedCss`; `10-probe.js` exposes the same for the in-page
route, and the Dom trait carries `linked_stylesheet_text`.
Also `enclosing_css_selector` blanks comments before hunting the previous
declaration delimiter, and `check_typography` reports the uniquely most-used
family instead of every family over a 15% share.
Verified: `impeccable detect --no-config --json tests/fixtures/antipatterns`
is now byte-identical to `node cli/bin/cli.js` on an origin/main worktree
over the shared corpus (432 findings). The two changed lines in
tests/oracle/vectors/calls/rules.checks/checkHtmlPatterns.jsonl were
re-recorded by running origin/main's `checkHtmlPatterns` over the frozen
args; only the comment-polluted selector changed. Goldens re-recorded for
the advisory partition (config-*, fixture gemini/gpt-tells,
numbered-section-labels, scoped-ignore, shape-assembled-illustration,
color, em-dash-entities) and the help text, each cross-checked against the
JS on origin/main.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
76 lines
2.8 KiB
Rust
76 lines
2.8 KiB
Rust
//! impeccable-wasm: the in-page rule core. wasm-bindgen exports over
|
|
//! `impeccable_core::browser` (rules driven through the JS DOM probe) and
|
|
//! over the pure `impeccable_core` functions (JSON in / JSON out).
|
|
|
|
pub mod dom_source;
|
|
#[cfg(feature = "detect")]
|
|
pub mod exports_detect;
|
|
pub mod exports_driver;
|
|
#[cfg(feature = "pure-exports")]
|
|
pub mod exports_pure;
|
|
pub mod exports_visual;
|
|
pub mod js_dom;
|
|
|
|
use dom_source::with_dom;
|
|
use impeccable_core::browser::driver;
|
|
use impeccable_core::browser::BrowserConfig;
|
|
use impeccable_core::rule_pack::RulePack;
|
|
use std::sync::OnceLock;
|
|
use wasm_bindgen::prelude::*;
|
|
|
|
static RULE_PACK: OnceLock<&'static dyn RulePack> = OnceLock::new();
|
|
|
|
/// Install a rule pack: registers its rows in the registry and hands its
|
|
/// hooks to every export below. Rust-only, on purpose — a pack is a compiled
|
|
/// dependency, not something JS passes in — so the caller is a downstream
|
|
/// crate that links this one as an rlib and calls this before its own exports
|
|
/// run. Later calls are ignored, the first pack wins.
|
|
///
|
|
/// The static HTML engine's half of a pack goes through
|
|
/// [`exports_detect::set_static_rule_pack`] (feature `detect`).
|
|
pub fn set_rule_pack(pack: &'static dyn RulePack) {
|
|
impeccable_core::rule_pack::install(pack);
|
|
let _ = RULE_PACK.set(pack);
|
|
}
|
|
|
|
/// The installed pack, if any.
|
|
pub fn installed_rule_pack() -> Option<&'static dyn RulePack> {
|
|
RULE_PACK.get().copied()
|
|
}
|
|
|
|
/// `collectBrowserFindings()`: `config_json` is `{ extensionMode,
|
|
/// disabledRules, designSystem, lineLengthMax }`; returns
|
|
/// `{ groups: [{ el, findings }], pageLevel: [...] }`.
|
|
#[wasm_bindgen]
|
|
pub fn collect_browser_findings(config_json: &str) -> String {
|
|
let mut config: BrowserConfig = serde_json::from_str(config_json).unwrap_or_default();
|
|
config.rule_pack = installed_rule_pack();
|
|
let out = with_dom(|dom| driver::collect_browser_findings(dom, &config));
|
|
serde_json::to_string(&out).unwrap_or_else(|_| "{\"groups\":[],\"pageLevel\":[]}".into())
|
|
}
|
|
|
|
/// `scopedIgnoreActive(el, ruleId)`.
|
|
#[wasm_bindgen]
|
|
pub fn scoped_ignore_active(el: u32, rule_id: &str) -> bool {
|
|
with_dom(|dom| driver::scoped_ignore_active(dom, el, rule_id))
|
|
}
|
|
|
|
/// The rule registry as JSON: `[{ id, name, category, severity, advisory, description }]`.
|
|
/// Built-ins in registry order, then any installed rule pack's rows.
|
|
#[wasm_bindgen]
|
|
pub fn antipatterns_json() -> String {
|
|
let rows: Vec<serde_json::Value> = impeccable_core::registry::all_antipatterns()
|
|
.map(|ap| {
|
|
serde_json::json!({
|
|
"id": ap.id,
|
|
"name": ap.name,
|
|
"category": ap.category,
|
|
"severity": ap.severity,
|
|
"advisory": ap.severity == Some("advisory"),
|
|
"description": ap.description,
|
|
})
|
|
})
|
|
.collect();
|
|
serde_json::to_string(&rows).unwrap_or_else(|_| "[]".into())
|
|
}
|