@soeditor/html-tools
v1.6.0
Published
Extensible HTML diagnostics and formatting for SoEditor.
Maintainers
Readme
@soeditor/html-tools
UI-independent HTML diagnostic providers, Problems aggregation, and explicit guarded Prettier formatting for SoEditor. Formatting is command-driven and refuses parser-invalid source; unknown elements and CMS markers are not diagnostic errors merely because they are unfamiliar.
Accessibility and SEO diagnostics
AccessibilityDiagnosticsPlugin and SeoDiagnosticsPlugin are independently
selectable plugins. Each automatically loads DiagnosticsPlugin, registers a
source-only provider, and contributes to the existing document.validate
command and DiagnosticsService result. They parse canonical HTML into
SoEditor-owned trees; they never render, execute, sanitize, fetch, or mutate the
source.
import {
AccessibilityDiagnosticsPlugin,
SeoDiagnosticsPlugin,
} from '@soeditor/html-tools';
const editor = await Editor.create({
data: '<!doctype html><html><head></head><body></body></html>',
plugins: [AccessibilityDiagnosticsPlugin, SeoDiagnosticsPlugin],
config: {
htmlTools: {
accessibility: {
rules: {
'a11y.heading-order': false,
'a11y.iframe-title': 'error',
},
},
seo: {
rules: {
'seo.h1': 'warning',
},
},
},
},
});
const problems = await editor.execute('document.validate');A rule setting is either false or error | warning | info | hint. Unknown
rule codes and malformed values fail editor initialization with an actionable
configuration error.
| Provider | Rule | Default | Scope |
| -------------------- | ----------------------- | ------- | ------------------------------------------------------------ |
| html.accessibility | a11y.form-label | warning | Visible HTML form controls without a native/ARIA label path. |
| html.accessibility | a11y.heading-order | warning | Heading-level jumps in complete documents only. |
| html.accessibility | a11y.iframe-title | warning | Iframes with a missing or empty title. |
| html.accessibility | a11y.interactive-name | warning | Buttons/button-like inputs without a detectable name. |
| html.seo | seo.document-title | warning | Missing, empty, or duplicate complete-document title. |
| html.seo | seo.h1 | info | Missing or multiple complete-document h1 elements. |
| html.seo | seo.meta-description | info | Missing or empty complete-document meta description. |
The base html.structure provider continues to report duplicate IDs, missing
image alt, and missing complete-document root lang. Template descendants
are treated as inert template data by the Phase 17 quality providers.
These rules are deliberately bounded source-analysis aids. They cannot inspect computed styles, contrast, focus order, screen-reader behavior, rendered scripts, application context, remote pages, search indexing, or ranking. Their results are not a claim of WCAG, legal, Lighthouse, or search-engine compliance.
Validation workflow
Validation remains manual by default through document.validate or
DiagnosticsService.validate(). Applications may opt into per-editor
debounced validation:
const editor = await Editor.create({
plugins: [DiagnosticsPlugin],
config: {
htmlTools: {
diagnostics: {
validation: { mode: 'debounced', delay: 300 },
},
},
},
});Each run snapshots the registered providers, runs them independently, and
retains registration order in the final Problems list. A rejected or malformed
provider is recorded in service.failures; it does not prevent other providers
from completing. Overlapping, unregistered, changed-document, and destroyed
runs cannot publish stale results.
service.snapshot exposes immutable idle | validating | ready state,
Problems, provider failures, and counts. service.subscribe() observes
published workflow transitions. service.getProblems(filter) and
service.getCounts(filter) accept optional provider and severity filters. The
original service.problems, register(), and validate() entry points remain
available.
Formatting worker and CSP
Formatting loads only when requested. The built package ships
dist/assets/formatting-worker-*.js; its URL is relative to the formatting
module, so nested deployments do not depend on a root /assets directory.
Keep the complete dist directory when publishing or copying the package.
Bundlers must retain the asset referenced by new URL(..., import.meta.url).
The worker uses a normal HTTP(S) URL, not blob: or data:. Same-origin
deployments work with worker-src 'self', or with a script-src 'self' fallback
when worker-src and child-src are absent. A separately hosted package requires
its resource origin to be allowed by the host policy and worker origin rules.
Worker startup failures retain the existing main-thread formatter fallback;
this does not bypass CSP or suppress browser policy diagnostics. Size limits,
parser protection, stale-result checks and undo behavior remain unchanged.
