@svelte-vitals/core 0.47.2 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Ct as RuleSetting, Dt as TreatDynamicAs, Et as Severity, Ot as Value, St as RuleOverride, at as formatGithubReport, bt as Result, dt as summarize, ft as CATEGORIES, gt as Fix, ht as Detection, it as formatMarkdownReport, jt as defineConfig, mt as Config, n as RuleEvidence, o as ScoreModel, pt as Category, st as Summary, t as JsonReport, ut as hasFailureAtOrAbove, vt as Presence, wt as RuleSettingObject, xt as RuleOptions } from "./json-
|
|
1
|
+
import { Ct as RuleSetting, Dt as TreatDynamicAs, Et as Severity, Ot as Value, St as RuleOverride, at as formatGithubReport, bt as Result, dt as summarize, ft as CATEGORIES, gt as Fix, ht as Detection, it as formatMarkdownReport, jt as defineConfig, mt as Config, n as RuleEvidence, o as ScoreModel, pt as Category, st as Summary, t as JsonReport, ut as hasFailureAtOrAbove, vt as Presence, wt as RuleSettingObject, xt as RuleOptions } from "./json-pC2tDNuR.js";
|
|
2
2
|
export { CATEGORIES, type Category, type Config, type Detection, type Fix, type JsonReport, type Presence, type Result, type RuleEvidence, type RuleOptions, type RuleOverride, type RuleSetting, type RuleSettingObject, type ScoreModel, type Severity, type Summary, type TreatDynamicAs, type Value, defineConfig, formatGithubReport, formatMarkdownReport, hasFailureAtOrAbove, summarize };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cr as summarize, dr as CATEGORIES, l as formatGithubReport, mr as defineConfig, sr as hasFailureAtOrAbove, t as formatMarkdownReport } from "./markdown-CP5TPy-s.js";
|
|
2
2
|
export { CATEGORIES, defineConfig, formatGithubReport, formatMarkdownReport, hasFailureAtOrAbove, summarize };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as HeadTag, A as formatFailedRuleWarning, At as defaultProject, B as IDREF_ATTRS, C as shouldSkipRangeCheck, D as applyOverrides, E as CompiledOverride, Et as Severity, F as withFailedRulesOff, Ft as OrphanEffectFact, G as isTopFragment, H as ResolvedA11y, I as KitModuleFacts, It as SourceSpan, J as HeadingInfo, K as splitTokens, L as A11yOccurrenceInfo, Lt as SuppressionDirective, M as selectRules, Mt as ComponentFacts, N as settingOptions, Nt as EachBlockFact, O as applyRuleSeverities, Ot as Value, P as settingSeverity, Pt as EffectFact, Q as HeadProvider, R as A11ySkipCause, Rt as skippedFileWarnings, S as resolveRuleOptions, T as validateRuleSetting, Tt as Scope, U as decodeFragmentId, V as LANDMARK_ROLES, W as foldOccurrences, X as ImageInfo, Y as ResolvedHeadings, Z as ResolvedImages, _ as RuleOptionsSpec, _t as KitAlias, a as HealthResult, at as formatGithubReport, b as listOption, bt as Result, c as ScoreResult, ct as classify, d as scoresByCategory, dt as summarize, et as ResolvedHead, f as Rule, g as RuleOptionSpec, gt as Fix, h as isPenalized, i as formatJsonReport, it as formatMarkdownReport, j as overrideMatches, k as compileOverrides, kt as defaultConfig, l as computeHealth, lt as effectiveSeverity, m as docsUrlFor, mt as Config, nt as Runtime, ot as Classification, p as RuleContext, pt as Category, q as stripTextDirective, r as buildJsonReport, rt as withReadLimit, s as ScoreOptions, t as JsonReport, tt as READ_CONCURRENCY, u as computeScore, ut as hasFailureAtOrAbove, v as intOption, w as validateRuleOptions, x as mapOption, y as isMentionedAnywhere, yt as Project, z as BranchStep } from "./json-
|
|
1
|
+
import { $ as HeadTag, A as formatFailedRuleWarning, At as defaultProject, B as IDREF_ATTRS, C as shouldSkipRangeCheck, D as applyOverrides, E as CompiledOverride, Et as Severity, F as withFailedRulesOff, Ft as OrphanEffectFact, G as isTopFragment, H as ResolvedA11y, I as KitModuleFacts, It as SourceSpan, J as HeadingInfo, K as splitTokens, L as A11yOccurrenceInfo, Lt as SuppressionDirective, M as selectRules, Mt as ComponentFacts, N as settingOptions, Nt as EachBlockFact, O as applyRuleSeverities, Ot as Value, P as settingSeverity, Pt as EffectFact, Q as HeadProvider, R as A11ySkipCause, Rt as skippedFileWarnings, S as resolveRuleOptions, T as validateRuleSetting, Tt as Scope, U as decodeFragmentId, V as LANDMARK_ROLES, W as foldOccurrences, X as ImageInfo, Y as ResolvedHeadings, Z as ResolvedImages, _ as RuleOptionsSpec, _t as KitAlias, a as HealthResult, at as formatGithubReport, b as listOption, bt as Result, c as ScoreResult, ct as classify, d as scoresByCategory, dt as summarize, et as ResolvedHead, f as Rule, g as RuleOptionSpec, gt as Fix, h as isPenalized, i as formatJsonReport, it as formatMarkdownReport, j as overrideMatches, k as compileOverrides, kt as defaultConfig, l as computeHealth, lt as effectiveSeverity, m as docsUrlFor, mt as Config, nt as Runtime, ot as Classification, p as RuleContext, pt as Category, q as stripTextDirective, r as buildJsonReport, rt as withReadLimit, s as ScoreOptions, t as JsonReport, tt as READ_CONCURRENCY, u as computeScore, ut as hasFailureAtOrAbove, v as intOption, w as validateRuleOptions, x as mapOption, y as isMentionedAnywhere, yt as Project, z as BranchStep } from "./json-pC2tDNuR.js";
|
|
2
2
|
import { AST } from "svelte/compiler";
|
|
3
3
|
//#region src/component-parse.d.ts
|
|
4
4
|
/**
|
|
@@ -537,6 +537,17 @@ declare const correctnessNonreactiveBuiltinState: Rule;
|
|
|
537
537
|
*/
|
|
538
538
|
declare const correctnessCheckableBindValue: Rule;
|
|
539
539
|
//#endregion
|
|
540
|
+
//#region src/rules/correctness/autoplay-muted.d.ts
|
|
541
|
+
/**
|
|
542
|
+
* correctness/autoplay-muted — Chrome and Safari block autoplay with audio, and a blocked
|
|
543
|
+
* `<video autoplay>` does not error: it silently never starts. The author sees it work in dev
|
|
544
|
+
* (after interacting with the page, autoplay is often allowed for the session) and ships a
|
|
545
|
+
* video that stays frozen for real visitors. `muted` in any form (bare, `muted={expr}`,
|
|
546
|
+
* `bind:muted`, spread) passes — expression values are not evaluated, per issue #580's scope —
|
|
547
|
+
* and only a literal `autoplay` counts.
|
|
548
|
+
*/
|
|
549
|
+
declare const correctnessAutoplayMuted: Rule;
|
|
550
|
+
//#endregion
|
|
540
551
|
//#region src/rules/correctness/orphan-effect.d.ts
|
|
541
552
|
declare const correctnessOrphanEffect: Rule;
|
|
542
553
|
//#endregion
|
|
@@ -722,6 +733,11 @@ declare const performanceSequentialAwaits: Rule;
|
|
|
722
733
|
*/
|
|
723
734
|
declare const performanceStateRaw: Rule;
|
|
724
735
|
//#endregion
|
|
736
|
+
//#region src/rules/perf/iframe-loading.d.ts
|
|
737
|
+
/** Any literal `loading` value passes (the author made a choice); an expression value is
|
|
738
|
+
* unknowable and passes; a spread could supply `loading` and passes. */
|
|
739
|
+
declare const performanceIframeLoading: Rule;
|
|
740
|
+
//#endregion
|
|
725
741
|
//#region src/rules/a11y/invalid-role.d.ts
|
|
726
742
|
declare const a11yInvalidRole: Rule;
|
|
727
743
|
//#endregion
|
|
@@ -752,6 +768,55 @@ declare const a11yPlaceholderLabelOption: Rule;
|
|
|
752
768
|
//#region src/rules/a11y/require-datetime.d.ts
|
|
753
769
|
declare const a11yRequireDatetime: Rule;
|
|
754
770
|
//#endregion
|
|
771
|
+
//#region src/rules/a11y/positive-tabindex.d.ts
|
|
772
|
+
/**
|
|
773
|
+
* Literal values only — an expression-valued `tabindex` is unknowable and passes. The
|
|
774
|
+
* Number()-based parse deliberately follows the Svelte compiler's a11y_positive_tabindex
|
|
775
|
+
* check so the scanner and the build agree (the docs page's Overlap section records the two
|
|
776
|
+
* divergences — bare `tabindex` and non-finite values, where this rule sides with the
|
|
777
|
+
* browser and stays silent). SVG elements are judged too, unlike the
|
|
778
|
+
* deprecated-* rules' `!inSvg` filter: SVG2 honours `tabindex` with identical focus-order
|
|
779
|
+
* semantics.
|
|
780
|
+
*/
|
|
781
|
+
declare const a11yPositiveTabindex: Rule;
|
|
782
|
+
//#endregion
|
|
783
|
+
//#region src/rules/a11y/no-accesskey.d.ts
|
|
784
|
+
/**
|
|
785
|
+
* a11y/no-accesskey — unlike most attribute rules, an expression-valued `accesskey` is reported
|
|
786
|
+
* too: the attribute's presence is the problem and its value never matters, so there is nothing
|
|
787
|
+
* unknowable about it. A spread-only carrier stays silent (the attribute never reaches `attrs`).
|
|
788
|
+
* SVG elements are judged along with HTML — `accesskey` is meaningless there, which makes its
|
|
789
|
+
* presence wrong in both namespaces.
|
|
790
|
+
*/
|
|
791
|
+
declare const a11yNoAccesskey: Rule;
|
|
792
|
+
//#endregion
|
|
793
|
+
//#region src/rules/a11y/no-autofocus.d.ts
|
|
794
|
+
/**
|
|
795
|
+
* a11y/no-autofocus — only a literal `autofocus` counts (a bare boolean attribute parses to
|
|
796
|
+
* `value: ''`); an expression value could be `false` and passes as unknowable. The ancestor walk
|
|
797
|
+
* uses `ElementFact.parent`, which looks through logic blocks but breaks at every non-lexical
|
|
798
|
+
* construct (a component, `{#snippet}` body, `<svelte:element>`, custom element, `{@html}`,
|
|
799
|
+
* `{@render}`, `<slot>`) — past a break the dialog cannot be proven, so the element is reported;
|
|
800
|
+
* the docs page's Limitations section names these classes and the inline-suppression escape
|
|
801
|
+
* hatch. SVG elements are judged along with HTML: SVG2 honours `autofocus` with the same
|
|
802
|
+
* page-load focus semantics.
|
|
803
|
+
*/
|
|
804
|
+
declare const a11yNoAutofocus: Rule;
|
|
805
|
+
//#endregion
|
|
806
|
+
//#region src/rules/a11y/no-duplicate-dt.d.ts
|
|
807
|
+
/**
|
|
808
|
+
* a11y/no-duplicate-dt — the candidate gathering (direct and div-wrapped `<dt>` children of a
|
|
809
|
+
* `<dl>`, fully-static names only, whitespace-normalized, case-sensitive) lives in the
|
|
810
|
+
* collector; see `collectDuplicateDts` in component-parse.ts for the exemption rationale.
|
|
811
|
+
*/
|
|
812
|
+
declare const a11yNoDuplicateDt: Rule;
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/rules/a11y/abbr-title.d.ts
|
|
815
|
+
declare const a11yAbbrTitle: Rule;
|
|
816
|
+
//#endregion
|
|
817
|
+
//#region src/rules/a11y/pattern-title.d.ts
|
|
818
|
+
declare const a11yPatternTitle: Rule;
|
|
819
|
+
//#endregion
|
|
755
820
|
//#region src/rules/a11y/deprecated-element.d.ts
|
|
756
821
|
declare const a11yDeprecatedElement: Rule;
|
|
757
822
|
//#endregion
|
|
@@ -1047,4 +1112,4 @@ declare function formatHtmlReport(results: Result[], config: Config, meta: {
|
|
|
1047
1112
|
coreVersion?: string;
|
|
1048
1113
|
}): string;
|
|
1049
1114
|
//#endregion
|
|
1050
|
-
export { type A11yOccurrenceInfo, type A11ySkipCause, APP_SCRIPT, APP_STYLE, type AppSnapshot, BAND_COLOR, type BranchStep, CHILD_NODE_KEYS, type Classification, type CompiledOverride, type ComponentFacts, type ConsoleReportOptions, type DirectiveIndex, type EachBlockFact, type EffectFact, type FailedRule, type HeadProvider, type HeadTag, type HeadingInfo, type HealthResult, IDREF_ATTRS, type ImageInfo, type KitAlias, type KitModuleFacts, LANDMARK_ROLES, type OrphanEffectFact, type Palette, type Project, READ_CONCURRENCY, ROBOTS_SOURCE_PATHS, type RawKitAliases, type ResolvedA11y, type ResolvedHead, type ResolvedHeadings, type ResolvedImages, type RouteBadge, type Rule, type RuleContext, RuleInfo, RuleOptionInfo, type RuleOptionSpec, type RuleOptionsSpec, type Runtime, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, type Scope, type ScoreOptions, type ScoreResult, type SourceSpan, type SuppressionDirective, VITE_CONFIG_FILES, type ViteKitConfigResult, a11yAccessibleName, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoMissingIdRef, a11yPermittedContents, a11yPlaceholderLabelOption, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, allRules, applyInlineDirectives, applyOverrides, applyRuleSeverities, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitAliasesInSvelteConfig, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isMentionedAnywhere, isPenalized, isTopFragment, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRepoLocalPath, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleOptions, validateRuleSetting, valueFromNodes, withFailedRulesOff, withReadLimit };
|
|
1115
|
+
export { type A11yOccurrenceInfo, type A11ySkipCause, APP_SCRIPT, APP_STYLE, type AppSnapshot, BAND_COLOR, type BranchStep, CHILD_NODE_KEYS, type Classification, type CompiledOverride, type ComponentFacts, type ConsoleReportOptions, type DirectiveIndex, type EachBlockFact, type EffectFact, type FailedRule, type HeadProvider, type HeadTag, type HeadingInfo, type HealthResult, IDREF_ATTRS, type ImageInfo, type KitAlias, type KitModuleFacts, LANDMARK_ROLES, type OrphanEffectFact, type Palette, type Project, READ_CONCURRENCY, ROBOTS_SOURCE_PATHS, type RawKitAliases, type ResolvedA11y, type ResolvedHead, type ResolvedHeadings, type ResolvedImages, type RouteBadge, type Rule, type RuleContext, RuleInfo, RuleOptionInfo, type RuleOptionSpec, type RuleOptionsSpec, type Runtime, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, type Scope, type ScoreOptions, type ScoreResult, type SourceSpan, type SuppressionDirective, VITE_CONFIG_FILES, type ViteKitConfigResult, a11yAbbrTitle, a11yAccessibleName, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoAccesskey, a11yNoAutofocus, a11yNoDuplicateDt, a11yNoMissingIdRef, a11yPatternTitle, a11yPermittedContents, a11yPlaceholderLabelOption, a11yPositiveTabindex, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, allRules, applyInlineDirectives, applyOverrides, applyRuleSeverities, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessAutoplayMuted, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitAliasesInSvelteConfig, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isMentionedAnywhere, isPenalized, isTopFragment, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceIframeLoading, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRepoLocalPath, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleOptions, validateRuleSetting, valueFromNodes, withFailedRulesOff, withReadLimit };
|
package/dist/internal.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as architectureRouteComponentImport, $n as seoOgTitle, $t as correctnessEachKey, A as a11yAbbrTitle, An as validateRuleSetting, At as textFromNodes, B as a11yInteractiveNesting, Bn as performanceRenderBlockingScript, Bt as correctnessServerBrowserGlobal, C as a11yPermittedContents, Cn as intOption, Ct as attrText, D as a11yDeprecatedAttr, Dn as resolveRuleOptions, Dt as findAttr, E as a11yDisallowedAriaProps, En as mapOption, Et as attrValueOf, F as a11yRequireDatetime, Fn as overrideMatches, Ft as securityServerModuleState, G as performanceIframeLoading, Gn as performanceResponsiveImage, Gt as correctnessCheckableBindValue, H as a11yRequiredAriaProps, Hn as performanceFontPreloadCrossorigin, Ht as correctnessOrphanLifecycle, I as a11yPlaceholderLabelOption, In as selectRules, It as securityHandlerStateWrite, J as performanceLoadWaterfall, Jn as imageRule, Jt as correctnessPropMutation, K as performanceStateRaw, Kn as performanceImageLoadingHint, Kt as correctnessNonreactiveBuiltinState, L as a11yUseList, Ln as settingOptions, Lt as securityJavascriptUrl, M as a11yNoAutofocus, Mn as applyRuleSeverities, Mt as architecturePropCount, N as a11yNoAccesskey, Nn as compileOverrides, Nt as architectureComponentSize, O as a11yDeprecatedElement, On as shouldSkipRangeCheck, Ot as lineOf, P as a11yPositiveTabindex, Pn as formatFailedRuleWarning, Pt as securitySharedStateImport, Q as architectureDocLinkTarget, Qn as seoJsonLd, Qt as correctnessEachIndexKey, R as a11yLabelHasControl, Rn as settingSeverity, Rt as securityRawHtml, S as a11yRequiredElement, Sn as performancePreconnect, St as CHILD_NODE_KEYS, T as a11yDeprecatedAria, Tn as listOption, Tt as attrValue, U as a11yUnknownAriaAttribute, Un as performancePreloadMissingAs, Ut as correctnessOrphanEffect, V as a11yInvalidAriaValue, Vn as performanceLcpImage, Vt as correctnessBasePathNavigation, W as a11yInvalidRole, Wn as linkRule, Wt as correctnessAutoplayMuted, X as performanceNamespaceImport, Xn as seoSitemapXml, Xt as correctnessEffectAsOnMount, Y as performanceMinifyDisabled, Yn as seoHtmlLang, Yt as correctnessUnmutatedState, Z as performanceHeavyImport, Zn as seoRobotsTxt, Zt as correctnessEffectAsDerived, _ as a11yNoMissingIdRef, _n as seoViewport, _t as decodeFragmentId, a as formatJsonReport, an as seoHreflang, ar as classify, at as collectTopLevelBindings, b as a11yDuplicateLandmark, bn as seoTwitterCard, bt as splitTokens, c as scoresByCategory, cn as seoDescriptionLength, cr as summarize, ct as resolveRepoLocalPath, d as messageText, dn as seoJsonLdPlaceholder, dt as collectSuppressions, en as seoSsrDisabled, er as seoOgImage, et as architectureReservedNamePlacement, f as ruleMetaById, fn as seoJsonLdDateFormat, fr as defaultConfig, ft as parseComponentFacts, g as a11yUnverifiedIdRef, gn as seoSitemapInRobots, gt as LANDMARK_ROLES, h as explainRule, hn as seoJsonLdValidity, ht as IDREF_ATTRS, i as buildJsonReport, in as seoSingleH1, ir as seoTitlePresence, it as architecturePrivateScopeImport, j as a11yNoDuplicateDt, jn as applyOverrides, jt as valueFromNodes, k as a11yPatternTitle, kn as validateRuleOptions, kt as parseSvelte, l as formatGithubReport, ln as seoTitleLength, lr as docsUrlFor, lt as resolveRunesModuleSpecifier, m as allRules, mn as seoJsonLdDeprecatedType, mt as unwrapTs, n as mdEscape, nn as seoDuplicateDescription, nr as seoDescriptionPresence, nt as architectureDirectoryNaming, o as computeHealth, on as seoImageAlt, or as effectiveSeverity, ot as parseInMemoryExports, p as severityToSarifLevel, pn as seoJsonLdRelativeUrl, pr as defaultProject, pt as parseModuleProgram, q as performanceSequentialAwaits, qn as performanceImageDimensions, qt as correctnessStalePropDerivation, r as terminalSafe, rn as seoDuplicateTitle, rr as headTagRule, rt as architectureUnitEntryFile, s as computeScore, sn as seoCharset, sr as hasFailureAtOrAbove, st as parseKitModuleFacts, t as formatMarkdownReport, tn as seoHeadingLevelSkip, tr as seoCanonicalUrl, tt as architectureReservedDirectoryNames, u as SEVERITY_RANK, un as seoJsonLdRequiredProps, ur as isPenalized, ut as collectNamedImportAliases, v as a11yIdDuplication, vn as seoOgUrl, vt as foldOccurrences, w as a11yDisallowedElement, wn as isMentionedAnywhere, wt as attrTextOf, x as a11yDoctype, xn as seoIndexability, xt as stripTextDirective, y as a11yTopLevelLandmark, yn as seoOgDescription, yt as isTopFragment, z as a11yAccessibleName, zn as withFailedRulesOff, zt as correctnessInstanceBrowserGlobal } from "./markdown-CP5TPy-s.js";
|
|
2
2
|
//#region src/inline-directives.ts
|
|
3
3
|
/** The directive silencing `r`, if one sits on its line and names its rule (or names nothing). */
|
|
4
4
|
function directiveFor(index, r) {
|
|
@@ -1707,4 +1707,4 @@ function formatHtmlReport(results, config, meta) {
|
|
|
1707
1707
|
return buildHtmlDocument(buildJsonReport(results, config, meta), meta);
|
|
1708
1708
|
}
|
|
1709
1709
|
//#endregion
|
|
1710
|
-
export { APP_SCRIPT, APP_STYLE, BAND_COLOR, CHILD_NODE_KEYS, IDREF_ATTRS, LANDMARK_ROLES, READ_CONCURRENCY, ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, a11yAccessibleName, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoMissingIdRef, a11yPermittedContents, a11yPlaceholderLabelOption, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, allRules, applyInlineDirectives, applyOverrides, applyRuleSeverities, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitAliasesInSvelteConfig, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isMentionedAnywhere, isPenalized, isTopFragment, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRepoLocalPath, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleOptions, validateRuleSetting, valueFromNodes, withFailedRulesOff, withReadLimit };
|
|
1710
|
+
export { APP_SCRIPT, APP_STYLE, BAND_COLOR, CHILD_NODE_KEYS, IDREF_ATTRS, LANDMARK_ROLES, READ_CONCURRENCY, ROBOTS_SOURCE_PATHS, SITEMAP_SOURCE_PATHS, SVELTE_CONFIG_FILES, VITE_CONFIG_FILES, a11yAbbrTitle, a11yAccessibleName, a11yDeprecatedAria, a11yDeprecatedAttr, a11yDeprecatedElement, a11yDisallowedAriaProps, a11yDisallowedElement, a11yDoctype, a11yDuplicateLandmark, a11yIdDuplication, a11yInteractiveNesting, a11yInvalidAriaValue, a11yInvalidRole, a11yLabelHasControl, a11yNoAccesskey, a11yNoAutofocus, a11yNoDuplicateDt, a11yNoMissingIdRef, a11yPatternTitle, a11yPermittedContents, a11yPlaceholderLabelOption, a11yPositiveTabindex, a11yRequireDatetime, a11yRequiredAriaProps, a11yRequiredElement, a11yTopLevelLandmark, a11yUnknownAriaAttribute, a11yUnverifiedIdRef, a11yUseList, allRules, applyInlineDirectives, applyOverrides, applyRuleSeverities, architectureComponentSize, architectureDirectoryNaming, architectureDocLinkTarget, architecturePrivateScopeImport, architecturePropCount, architectureReservedDirectoryNames, architectureReservedNamePlacement, architectureRouteComponentImport, architectureUnitEntryFile, attrText, attrTextOf, attrValue, attrValueOf, buildHtmlDocument, buildJsonReport, classify, collectComponentFacts, collectKitModuleFacts, collectSourceFiles, collectSuppressions, compileOverrides, computeHealth, computeScore, correctnessAutoplayMuted, correctnessBasePathNavigation, correctnessCheckableBindValue, correctnessEachIndexKey, correctnessEachKey, correctnessEffectAsDerived, correctnessEffectAsOnMount, correctnessInstanceBrowserGlobal, correctnessNonreactiveBuiltinState, correctnessOrphanEffect, correctnessOrphanLifecycle, correctnessPropMutation, correctnessServerBrowserGlobal, correctnessStalePropDerivation, correctnessUnmutatedState, decodeFragmentId, defaultConfig, defaultProject, docsUrlFor, effectiveSeverity, emptyComponentFacts, emptyKitModuleFacts, escapeHtml, explainRule, findAttr, findKitAliasesInSvelteConfig, findKitPathsBaseInSvelteConfig, findKitPathsBaseInViteConfig, findMinifyDisabled, foldOccurrences, formatAgentReport, formatConsoleReport, formatFailedRuleWarning, formatGithubReport, formatHtmlReport, formatJsonReport, formatMarkdownReport, formatSarifReport, hasFailureAtOrAbove, headTagRule, imageRule, intOption, isMentionedAnywhere, isPenalized, isTopFragment, lineOf, linkRule, listOption, mapOption, noColorPalette, overrideMatches, parseComponentFacts, parseKitModuleFacts, parseSvelte, performanceFontPreloadCrossorigin, performanceHeavyImport, performanceIframeLoading, performanceImageDimensions, performanceImageLoadingHint, performanceLcpImage, performanceLoadWaterfall, performanceMinifyDisabled, performanceNamespaceImport, performancePreconnect, performancePreloadMissingAs, performanceRenderBlockingScript, performanceResponsiveImage, performanceSequentialAwaits, performanceStateRaw, renderAppShell, resolveKitAliases, resolveKitPathsBase, resolveRepoLocalPath, resolveRuleOptions, resolveRunesModuleSpecifier, runRules, safeHref, scoreBand, scoreColor, scoresByCategory, securityHandlerStateWrite, securityJavascriptUrl, securityRawHtml, securityServerModuleState, securitySharedStateImport, selectRules, seoCanonicalUrl, seoCharset, seoDescriptionLength, seoDescriptionPresence, seoDuplicateDescription, seoDuplicateTitle, seoHeadingLevelSkip, seoHreflang, seoHtmlLang, seoImageAlt, seoIndexability, seoJsonLd, seoJsonLdDateFormat, seoJsonLdDeprecatedType, seoJsonLdPlaceholder, seoJsonLdRelativeUrl, seoJsonLdRequiredProps, seoJsonLdValidity, seoOgDescription, seoOgImage, seoOgTitle, seoOgUrl, seoRobotsTxt, seoSingleH1, seoSitemapInRobots, seoSitemapXml, seoSsrDisabled, seoTitleLength, seoTitlePresence, seoTwitterCard, seoViewport, settingOptions, settingSeverity, shouldSkipRangeCheck, skippedFileWarnings, splitTokens, stripTextDirective, summarize, terminalSafe, textFromNodes, unknownDirectiveIds, validateRuleOptions, validateRuleSetting, valueFromNodes, withFailedRulesOff, withReadLimit };
|
|
@@ -284,6 +284,15 @@ interface ComponentFacts {
|
|
|
284
284
|
line: number;
|
|
285
285
|
text: string;
|
|
286
286
|
}[];
|
|
287
|
+
/** `<video>` with a literal `autoplay` attribute and no `muted` in any form (correctness/autoplay-muted). */
|
|
288
|
+
videosAutoplayNoMuted?: {
|
|
289
|
+
line: number;
|
|
290
|
+
}[];
|
|
291
|
+
/** `<dt>` elements whose static name duplicates an earlier sibling `<dt>` in the same `<dl>` (a11y/no-duplicate-dt). */
|
|
292
|
+
duplicateDts?: {
|
|
293
|
+
line: number;
|
|
294
|
+
text: string;
|
|
295
|
+
}[];
|
|
287
296
|
/** Set when the file failed to read or parse and these facts are the empty fallback — the file was NOT analyzed. */
|
|
288
297
|
parseFailed?: true;
|
|
289
298
|
/** Set when the file could not be READ — an environment problem (permissions, a descriptor
|
|
@@ -2559,6 +2559,35 @@ const correctnessCheckableBindValue = componentRule({
|
|
|
2559
2559
|
}))
|
|
2560
2560
|
});
|
|
2561
2561
|
//#endregion
|
|
2562
|
+
//#region src/rules/correctness/autoplay-muted.ts
|
|
2563
|
+
/**
|
|
2564
|
+
* correctness/autoplay-muted — Chrome and Safari block autoplay with audio, and a blocked
|
|
2565
|
+
* `<video autoplay>` does not error: it silently never starts. The author sees it work in dev
|
|
2566
|
+
* (after interacting with the page, autoplay is often allowed for the session) and ships a
|
|
2567
|
+
* video that stays frozen for real visitors. `muted` in any form (bare, `muted={expr}`,
|
|
2568
|
+
* `bind:muted`, spread) passes — expression values are not evaluated, per issue #580's scope —
|
|
2569
|
+
* and only a literal `autoplay` counts.
|
|
2570
|
+
*/
|
|
2571
|
+
const correctnessAutoplayMuted = componentRule({
|
|
2572
|
+
id: "correctness/autoplay-muted",
|
|
2573
|
+
title: "Autoplay video without muted",
|
|
2574
|
+
category: "correctness",
|
|
2575
|
+
severity: "warning",
|
|
2576
|
+
label: "Muted autoplay videos",
|
|
2577
|
+
recommendation: "Add `muted` to every `<video autoplay>` (and typically `playsinline`, so iOS plays it inline instead of refusing or going fullscreen).",
|
|
2578
|
+
rationale: "Browser autoplay policies block autoplay with audio: Chrome and Safari only honour `autoplay` when the video is muted (or the site has earned an autoplay allowance; a video with no audio track may also be allowed). A blocked autoplay does not throw — the video just never starts, so the page ships with a frozen poster frame for real visitors while working in development, where prior interaction often unlocks autoplay for the session. Adding `muted` is harmless even where autoplay would have been allowed.",
|
|
2579
|
+
fix: {
|
|
2580
|
+
description: "Add the muted attribute (and typically playsinline) to the autoplaying video.",
|
|
2581
|
+
snippet: "<video autoplay muted playsinline src=\"/hero.mp4\"></video>",
|
|
2582
|
+
lang: "svelte"
|
|
2583
|
+
},
|
|
2584
|
+
applies: (c) => (c.videosAutoplayNoMuted ?? []).length > 0,
|
|
2585
|
+
bad: (c) => (c.videosAutoplayNoMuted ?? []).map((f) => ({
|
|
2586
|
+
line: f.line,
|
|
2587
|
+
message: "<video autoplay> without muted — browsers block audible autoplay, so the video silently never starts playing"
|
|
2588
|
+
}))
|
|
2589
|
+
});
|
|
2590
|
+
//#endregion
|
|
2562
2591
|
//#region src/rules/correctness/orphan-effect.ts
|
|
2563
2592
|
const correctnessOrphanEffect = componentRule({
|
|
2564
2593
|
id: "correctness/orphan-effect",
|
|
@@ -3411,14 +3440,29 @@ function isInteractiveElement(tag, attrs) {
|
|
|
3411
3440
|
return false;
|
|
3412
3441
|
}
|
|
3413
3442
|
if ((tag === "audio" || tag === "video") && attrs.some((a) => a.name === "controls")) return true;
|
|
3414
|
-
const
|
|
3415
|
-
if (
|
|
3416
|
-
const n = Number(tabindex);
|
|
3417
|
-
if (Number.isFinite(n) && n >= 0) return true;
|
|
3418
|
-
}
|
|
3443
|
+
const n = literalTabindexValue(literalOf(attrs, "tabindex"));
|
|
3444
|
+
if (n !== void 0 && n >= 0) return true;
|
|
3419
3445
|
return hasRoleIn(attrs, INTERACTIVE_ROLES);
|
|
3420
3446
|
}
|
|
3421
3447
|
/**
|
|
3448
|
+
* Finite numeric value of a literal `tabindex` per `Number()` semantics, or undefined when
|
|
3449
|
+
* blank, unparsable, or non-finite. A blank tabindex is invalid HTML and ignored by
|
|
3450
|
+
* browsers (`Number('')` would coerce to 0). Deliberately NOT HTML's
|
|
3451
|
+
* rules-for-parsing-integers: `Number()` is what the Svelte compiler's
|
|
3452
|
+
* a11y_positive_tabindex check uses too, and disagreeing with the user's build would make
|
|
3453
|
+
* the rule noise (the cost is edge divergence from browsers, e.g. `"1abc"` — browser
|
|
3454
|
+
* tabIndex 1 — parses to undefined here). The finiteness guard is this helper's one
|
|
3455
|
+
* departure from the compiler's `!isNaN(v) && +v > 0`: `"Infinity"`/overflowing values make
|
|
3456
|
+
* the compiler warn while browsers ignore them, so undefined is the correct answer here.
|
|
3457
|
+
* Shared by the interactive-element classification above and a11y/positive-tabindex.
|
|
3458
|
+
*/
|
|
3459
|
+
function literalTabindexValue(raw) {
|
|
3460
|
+
const trimmed = raw?.trim();
|
|
3461
|
+
if (!trimmed) return void 0;
|
|
3462
|
+
const n = Number(trimmed);
|
|
3463
|
+
return Number.isFinite(n) ? n : void 0;
|
|
3464
|
+
}
|
|
3465
|
+
/**
|
|
3422
3466
|
* Whether `tag` opens a new "interactive container" for a11y/interactive-nesting's nesting
|
|
3423
3467
|
* stack — a narrower set than `isInteractiveElement`: only `a`-with-`href`, `button`, and a
|
|
3424
3468
|
* literal `CONTAINER_ROLES` role (an `<input>` or `tabindex` div is itself a valid nesting
|
|
@@ -4300,7 +4344,8 @@ function collectElements(node, source, acc, inSvg, parent) {
|
|
|
4300
4344
|
tag,
|
|
4301
4345
|
line: lineOf(source, node.start),
|
|
4302
4346
|
attrs: node.attributes.filter((a) => a?.type === "Attribute" && typeof a.name === "string").map((a) => {
|
|
4303
|
-
const
|
|
4347
|
+
const cls = classifyAttrValue(a.value);
|
|
4348
|
+
const value = "expression" in cls ? void 0 : cls.literal;
|
|
4304
4349
|
return {
|
|
4305
4350
|
name: String(a.name).toLowerCase(),
|
|
4306
4351
|
line: lineOf(source, a.start ?? node.start),
|
|
@@ -4836,6 +4881,114 @@ function collectTimesMissingDatetime(node, source, acc) {
|
|
|
4836
4881
|
for (const key of CHILD_NODE_KEYS) if (key in node) collectTimesMissingDatetime(node[key], source, acc);
|
|
4837
4882
|
}
|
|
4838
4883
|
/**
|
|
4884
|
+
* Fully-static text content of a node list: concatenated `Text` data, looked through static
|
|
4885
|
+
* regular elements (`<dt><code>HTTP</code></dt>` names "HTTP"), with comments contributing
|
|
4886
|
+
* nothing. `undefined` when anything dynamic appears anywhere below — an expression, a
|
|
4887
|
+
* component, a block, `{@html}`/`{@render}`, `<svelte:element>`, a `<slot>`, or a custom
|
|
4888
|
+
* element (its shadow root may supply content) — because the rendered text is then unknowable.
|
|
4889
|
+
*/
|
|
4890
|
+
function staticTextOf(nodes) {
|
|
4891
|
+
let out = "";
|
|
4892
|
+
for (const n of nodes) {
|
|
4893
|
+
if (!n || typeof n !== "object") continue;
|
|
4894
|
+
if (n.type === "Comment") continue;
|
|
4895
|
+
if (n.type === "Text") {
|
|
4896
|
+
out += String(n.data ?? "");
|
|
4897
|
+
continue;
|
|
4898
|
+
}
|
|
4899
|
+
if (n.type === "RegularElement" && typeof n.name === "string" && !n.name.includes("-")) {
|
|
4900
|
+
const inner = staticTextOf(n.fragment?.nodes ?? []);
|
|
4901
|
+
if (inner === void 0) return void 0;
|
|
4902
|
+
out += inner;
|
|
4903
|
+
continue;
|
|
4904
|
+
}
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
return out;
|
|
4908
|
+
}
|
|
4909
|
+
/**
|
|
4910
|
+
* Duplicate `<dt>` names within one `<dl>` (a11y/no-duplicate-dt). Candidates are `<dt>`
|
|
4911
|
+
* elements that are direct children of the `<dl>`, or direct children of a direct `<div>`
|
|
4912
|
+
* child (the spec's div-wrapped name-value groups); a dt under a logic block or component is
|
|
4913
|
+
* not a candidate — its multiplicity or rendered content is unknowable, and unknowable names
|
|
4914
|
+
* can only ever add duplicates, never fabricate one between two static non-duplicates. A
|
|
4915
|
+
* candidate's name is its fully-static text content (`staticTextOf` above — static phrasing
|
|
4916
|
+
* markup like `<dt><code>HTTP</code></dt>` is looked through, comments contribute nothing),
|
|
4917
|
+
* trimmed and whitespace-collapsed; an empty name is not a candidate (two empty dts are a
|
|
4918
|
+
* missing-content defect, not a duplicate name).
|
|
4919
|
+
* Comparison is case-sensitive — the spec leaves the equality undefined, so only certain
|
|
4920
|
+
* duplicates are reported. Namespace is tracked like `collectElements`: a `<dl>` inside
|
|
4921
|
+
* `<svg>` never renders as an HTML definition list, so it is skipped; `<foreignObject>`
|
|
4922
|
+
* children return to HTML. Nested `<dl>`s get their own scope for free — the walk recurses
|
|
4923
|
+
* and a nested dl's dts are never the outer dl's direct children.
|
|
4924
|
+
*/
|
|
4925
|
+
function collectDuplicateDts(node, source, acc, inSvg) {
|
|
4926
|
+
if (Array.isArray(node)) {
|
|
4927
|
+
for (const child of node) collectDuplicateDts(child, source, acc, inSvg);
|
|
4928
|
+
return;
|
|
4929
|
+
}
|
|
4930
|
+
if (!node || typeof node !== "object") return;
|
|
4931
|
+
let next = inSvg;
|
|
4932
|
+
if (node.type === "RegularElement" && typeof node.name === "string") {
|
|
4933
|
+
const tag = node.name.toLowerCase();
|
|
4934
|
+
if (tag === "svg") next = true;
|
|
4935
|
+
else if (tag === "foreignobject") next = false;
|
|
4936
|
+
if (!inSvg && tag === "dl") {
|
|
4937
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4938
|
+
const judge = (dt) => {
|
|
4939
|
+
const raw = staticTextOf(dt.fragment?.nodes ?? []);
|
|
4940
|
+
if (raw === void 0) return;
|
|
4941
|
+
const name = raw.trim().replace(/\s+/g, " ");
|
|
4942
|
+
if (!name) return;
|
|
4943
|
+
if (seen.has(name)) acc.push({
|
|
4944
|
+
line: lineOf(source, dt.start),
|
|
4945
|
+
text: name
|
|
4946
|
+
});
|
|
4947
|
+
else seen.add(name);
|
|
4948
|
+
};
|
|
4949
|
+
for (const child of node.fragment?.nodes ?? []) {
|
|
4950
|
+
if (child?.type !== "RegularElement" || typeof child.name !== "string") continue;
|
|
4951
|
+
const childTag = child.name.toLowerCase();
|
|
4952
|
+
if (childTag === "dt") judge(child);
|
|
4953
|
+
else if (childTag === "div") {
|
|
4954
|
+
for (const inner of child.fragment?.nodes ?? []) if (inner?.type === "RegularElement" && typeof inner.name === "string" && inner.name.toLowerCase() === "dt") judge(inner);
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
for (const key of CHILD_NODE_KEYS) if (key in node) collectDuplicateDts(node[key], source, acc, next);
|
|
4960
|
+
}
|
|
4961
|
+
/**
|
|
4962
|
+
* `<video>` with a literal `autoplay` attribute and no `muted` in any form
|
|
4963
|
+
* (correctness/autoplay-muted). Presence is what autoplays, so any literal value counts
|
|
4964
|
+
* (`autoplay="false"` still autoplays); an expression value is unknowable and is skipped.
|
|
4965
|
+
* `muted` passes in every form — bare, `bind:muted`, a spread that could supply it, or
|
|
4966
|
+
* `muted={expr}`, whose value is deliberately not evaluated (issue #580 scopes the rule to
|
|
4967
|
+
* attribute presence). Only `RegularElement` is checked — `<svelte:element this="video">`
|
|
4968
|
+
* follows the same RegularElement-only convention as `collectCheckableBindValues`. Namespace
|
|
4969
|
+
* is tracked like `collectElements`: a `<video>` inside `<svg>` is an SVG-namespace element,
|
|
4970
|
+
* not an HTML video, so it is skipped; `<foreignObject>` children return to HTML.
|
|
4971
|
+
*/
|
|
4972
|
+
function collectVideosAutoplayNoMuted(node, source, acc, inSvg) {
|
|
4973
|
+
if (Array.isArray(node)) {
|
|
4974
|
+
for (const child of node) collectVideosAutoplayNoMuted(child, source, acc, inSvg);
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
if (!node || typeof node !== "object") return;
|
|
4978
|
+
let next = inSvg;
|
|
4979
|
+
if (node.type === "RegularElement" && typeof node.name === "string") {
|
|
4980
|
+
const tag = node.name.toLowerCase();
|
|
4981
|
+
if (tag === "svg") next = true;
|
|
4982
|
+
else if (tag === "foreignobject") next = false;
|
|
4983
|
+
if (!inSvg && tag === "video" && Array.isArray(node.attributes)) {
|
|
4984
|
+
const literalAutoplay = attrText(node.attributes, "autoplay") !== void 0;
|
|
4985
|
+
const hasMuted = findAttr(node.attributes, "muted") !== void 0 || node.attributes.some((a) => a?.type === "BindDirective" && a.name === "muted" || a?.type === "SpreadAttribute");
|
|
4986
|
+
if (literalAutoplay && !hasMuted) acc.push({ line: lineOf(source, node.start) });
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
for (const key of CHILD_NODE_KEYS) if (key in node) collectVideosAutoplayNoMuted(node[key], source, acc, next);
|
|
4990
|
+
}
|
|
4991
|
+
/**
|
|
4839
4992
|
* Root-relative `<a href="/…">` literals (correctness/base-path-navigation). Only
|
|
4840
4993
|
* `RegularElement` anchors with a fully static href are considered, which is what makes the
|
|
4841
4994
|
* correct forms self-excluding: `href="{base}/x"` and `href={resolve('/x')}` contain an
|
|
@@ -5692,6 +5845,10 @@ function parseComponentFacts(source, filename) {
|
|
|
5692
5845
|
collectSelectsMissingPlaceholder(ast.fragment ?? ast, source, selectsMissingPlaceholder);
|
|
5693
5846
|
const timesMissingDatetime = [];
|
|
5694
5847
|
collectTimesMissingDatetime(ast.fragment ?? ast, source, timesMissingDatetime);
|
|
5848
|
+
const videosAutoplayNoMuted = [];
|
|
5849
|
+
collectVideosAutoplayNoMuted(ast.fragment ?? ast, source, videosAutoplayNoMuted, ast.options?.namespace === "svg");
|
|
5850
|
+
const duplicateDts = [];
|
|
5851
|
+
collectDuplicateDts(ast.fragment ?? ast, source, duplicateDts, ast.options?.namespace === "svg");
|
|
5695
5852
|
const basePathLinks = [];
|
|
5696
5853
|
collectHrefLinks(ast.fragment ?? ast, source, basePathLinks);
|
|
5697
5854
|
const gotoPrograms = [ast.module?.content, ast.instance?.content].filter(Boolean);
|
|
@@ -5903,7 +6060,9 @@ function parseComponentFacts(source, filename) {
|
|
|
5903
6060
|
unassociatedLabels,
|
|
5904
6061
|
bulletTexts,
|
|
5905
6062
|
selectsMissingPlaceholder,
|
|
5906
|
-
timesMissingDatetime
|
|
6063
|
+
timesMissingDatetime,
|
|
6064
|
+
videosAutoplayNoMuted,
|
|
6065
|
+
duplicateDts
|
|
5907
6066
|
};
|
|
5908
6067
|
}
|
|
5909
6068
|
//#endregion
|
|
@@ -8192,6 +8351,29 @@ const performanceStateRaw = componentRule({
|
|
|
8192
8351
|
}))
|
|
8193
8352
|
});
|
|
8194
8353
|
//#endregion
|
|
8354
|
+
//#region src/rules/perf/iframe-loading.ts
|
|
8355
|
+
/** Any literal `loading` value passes (the author made a choice); an expression value is
|
|
8356
|
+
* unknowable and passes; a spread could supply `loading` and passes. */
|
|
8357
|
+
const performanceIframeLoading = componentRule({
|
|
8358
|
+
id: "performance/iframe-loading",
|
|
8359
|
+
title: "Iframe loading attribute",
|
|
8360
|
+
category: "performance",
|
|
8361
|
+
severity: "info",
|
|
8362
|
+
label: "<iframe> loading attribute",
|
|
8363
|
+
recommendation: "If this iframe can be offscreen on load, add loading=\"lazy\"; keep an above-the-fold iframe eager.",
|
|
8364
|
+
rationale: "An iframe without a loading attribute loads eagerly, and an offscreen iframe (embedded video player, map, ad slot) typically loads an entire third-party document — scripts, fonts, media — so its bandwidth and main-thread cost is usually larger than an offscreen image’s. loading=\"lazy\" defers it until the viewport approaches. Static analysis cannot tell whether the iframe is above the fold, so this is advisory.",
|
|
8365
|
+
fix: {
|
|
8366
|
+
description: "Add loading=\"lazy\" to iframes that can be offscreen on load.",
|
|
8367
|
+
snippet: "<iframe src=\"https://example.com/embed\" title=\"…\" loading=\"lazy\"></iframe>",
|
|
8368
|
+
lang: "svelte"
|
|
8369
|
+
},
|
|
8370
|
+
applies: (c) => (c.elements ?? []).some((e) => !e.inSvg && e.tag === "iframe"),
|
|
8371
|
+
bad: (c) => (c.elements ?? []).filter((e) => !e.inSvg && e.tag === "iframe" && !e.hasSpread && !e.attrs.some((a) => a.name === "loading")).map((e) => ({
|
|
8372
|
+
line: e.line,
|
|
8373
|
+
message: "<iframe> without a loading attribute loads eagerly even when offscreen"
|
|
8374
|
+
}))
|
|
8375
|
+
});
|
|
8376
|
+
//#endregion
|
|
8195
8377
|
//#region src/rules/a11y/invalid-role.ts
|
|
8196
8378
|
const a11yInvalidRole = componentRule({
|
|
8197
8379
|
id: "a11y/invalid-role",
|
|
@@ -8330,18 +8512,54 @@ const a11yInteractiveNesting = componentRule({
|
|
|
8330
8512
|
});
|
|
8331
8513
|
//#endregion
|
|
8332
8514
|
//#region src/rules/a11y/accessible-name.ts
|
|
8515
|
+
const NAMING_ATTRS = /* @__PURE__ */ new Set([
|
|
8516
|
+
"title",
|
|
8517
|
+
"aria-label",
|
|
8518
|
+
"aria-labelledby"
|
|
8519
|
+
]);
|
|
8520
|
+
/**
|
|
8521
|
+
* The iframe arm reads the generic elements channel rather than the collector's
|
|
8522
|
+
* `unnamedInteractive` facts: an iframe has no name-from-content step (its children are fallback
|
|
8523
|
+
* content, never rendered), so the subtree scan the other targets need does not apply — and the
|
|
8524
|
+
* channel already carries the SVG-namespace flag (SVG has no iframe; the element never renders)
|
|
8525
|
+
* and the spread guard. Hidden or presentational iframes are skipped — `aria-hidden="true"`,
|
|
8526
|
+
* `hidden`, `role="presentation"`/`"none"` — the tracking/analytics-frame class where a name
|
|
8527
|
+
* helps nobody; an expression value in any of the skip attributes (and `hidden` in any form,
|
|
8528
|
+
* bare included) resolves unknowable → silence, this rule's convention throughout. The skip set
|
|
8529
|
+
* is deliberate to this arm only: a hidden focusable button is a different defect.
|
|
8530
|
+
*/
|
|
8531
|
+
function unnamedIframes(c) {
|
|
8532
|
+
return (c.elements ?? []).filter((e) => {
|
|
8533
|
+
if (e.tag !== "iframe" || e.inSvg || e.hasSpread) return false;
|
|
8534
|
+
if (e.attrs.some((a) => NAMING_ATTRS.has(a.name) && (a.value === void 0 || a.value.trim() !== ""))) return false;
|
|
8535
|
+
const attr = (name) => e.attrs.find((a) => a.name === name);
|
|
8536
|
+
const ariaHidden = attr("aria-hidden");
|
|
8537
|
+
if (ariaHidden && (ariaHidden.value === void 0 || ariaHidden.value.trim().toLowerCase() === "true")) return false;
|
|
8538
|
+
if (attr("hidden")) return false;
|
|
8539
|
+
const role = attr("role");
|
|
8540
|
+
if (role) {
|
|
8541
|
+
if (role.value === void 0) return false;
|
|
8542
|
+
const resolved = resolveRole(splitTokens(role.value.toLowerCase()));
|
|
8543
|
+
if (resolved === "presentation" || resolved === "none") return false;
|
|
8544
|
+
}
|
|
8545
|
+
return true;
|
|
8546
|
+
});
|
|
8547
|
+
}
|
|
8333
8548
|
const a11yAccessibleName = componentRule({
|
|
8334
8549
|
id: "a11y/accessible-name",
|
|
8335
8550
|
title: "Interactive element has no accessible name",
|
|
8336
8551
|
category: "a11y",
|
|
8337
8552
|
label: "Accessible names",
|
|
8338
|
-
rationale: "A button, link, or image button with no accessible name is announced by assistive technology as its bare role (\"button\", \"link\") with nothing to distinguish it from any other control on the page.",
|
|
8339
|
-
recommendation: "Give the element visible text or an aria-label/aria-labelledby; a button or link whose only content is an icon image is named by that image's alt,
|
|
8340
|
-
applies: (c) => (c.unnamedInteractive ?? []).length > 0,
|
|
8341
|
-
bad: (c) => (c.unnamedInteractive ?? []).map((f) => ({
|
|
8553
|
+
rationale: "A button, link, or image button with no accessible name is announced by assistive technology as its bare role (\"button\", \"link\") with nothing to distinguish it from any other control on the page; an iframe without one is announced as an unnamed frame, with no way to tell an embedded video from an ad slot before entering it.",
|
|
8554
|
+
recommendation: "Give the element visible text or an aria-label/aria-labelledby; a button or link whose only content is an icon image is named by that image's alt, an <input type=\"image\"> by its own alt, and an <iframe> by its title.",
|
|
8555
|
+
applies: (c) => (c.unnamedInteractive ?? []).length > 0 || unnamedIframes(c).length > 0,
|
|
8556
|
+
bad: (c) => [...(c.unnamedInteractive ?? []).map((f) => ({
|
|
8342
8557
|
line: f.line,
|
|
8343
8558
|
message: `<${f.tag}> has no accessible name`
|
|
8344
|
-
}))
|
|
8559
|
+
})), ...unnamedIframes(c).map((e) => ({
|
|
8560
|
+
line: e.line,
|
|
8561
|
+
message: "<iframe> has no accessible name"
|
|
8562
|
+
}))]
|
|
8345
8563
|
});
|
|
8346
8564
|
//#endregion
|
|
8347
8565
|
//#region src/rules/a11y/label-has-control.ts
|
|
@@ -8406,6 +8624,217 @@ const a11yRequireDatetime = componentRule({
|
|
|
8406
8624
|
}))
|
|
8407
8625
|
});
|
|
8408
8626
|
//#endregion
|
|
8627
|
+
//#region src/rules/a11y/positive-tabindex.ts
|
|
8628
|
+
/**
|
|
8629
|
+
* Literal values only — an expression-valued `tabindex` is unknowable and passes. The
|
|
8630
|
+
* Number()-based parse deliberately follows the Svelte compiler's a11y_positive_tabindex
|
|
8631
|
+
* check so the scanner and the build agree (the docs page's Overlap section records the two
|
|
8632
|
+
* divergences — bare `tabindex` and non-finite values, where this rule sides with the
|
|
8633
|
+
* browser and stays silent). SVG elements are judged too, unlike the
|
|
8634
|
+
* deprecated-* rules' `!inSvg` filter: SVG2 honours `tabindex` with identical focus-order
|
|
8635
|
+
* semantics.
|
|
8636
|
+
*/
|
|
8637
|
+
const a11yPositiveTabindex = componentRule({
|
|
8638
|
+
id: "a11y/positive-tabindex",
|
|
8639
|
+
title: "Positive tabindex",
|
|
8640
|
+
category: "a11y",
|
|
8641
|
+
severity: "warning",
|
|
8642
|
+
label: "Tabindex values",
|
|
8643
|
+
recommendation: "Use `tabindex=\"0\"` and put elements in DOM order for the natural tab sequence, or `tabindex=\"-1\"` for elements focused programmatically. A positive value is essentially never intentional-and-correct.",
|
|
8644
|
+
rationale: "A tabindex greater than 0 puts the element ahead of every naturally-ordered element on the page — a single tabindex=\"1\" reorders keyboard navigation for the whole document, and the damage compounds with every element added later. The tab order can then diverge from the visual order, which WCAG 2.4.3 (Focus Order) requires to stay meaningful.",
|
|
8645
|
+
fix: { description: "Replace the positive tabindex with tabindex=\"0\" and move the element into DOM order, or tabindex=\"-1\" if it is only focused programmatically." },
|
|
8646
|
+
applies: (c) => (c.elements ?? []).some((e) => e.attrs.some((a) => a.name === "tabindex" && literalTabindexValue(a.value) !== void 0)),
|
|
8647
|
+
bad: (c) => (c.elements ?? []).flatMap((e) => {
|
|
8648
|
+
const raw = e.attrs.find((a) => a.name === "tabindex")?.value;
|
|
8649
|
+
if (raw === void 0) return [];
|
|
8650
|
+
const n = literalTabindexValue(raw);
|
|
8651
|
+
if (n === void 0 || n <= 0) return [];
|
|
8652
|
+
return [{
|
|
8653
|
+
line: e.line,
|
|
8654
|
+
message: `tabindex="${raw.trim()}" on <${e.tag}> hijacks the tab order for the whole page — only 0 and -1 are safe values`
|
|
8655
|
+
}];
|
|
8656
|
+
})
|
|
8657
|
+
});
|
|
8658
|
+
//#endregion
|
|
8659
|
+
//#region src/rules/a11y/no-accesskey.ts
|
|
8660
|
+
/**
|
|
8661
|
+
* a11y/no-accesskey — unlike most attribute rules, an expression-valued `accesskey` is reported
|
|
8662
|
+
* too: the attribute's presence is the problem and its value never matters, so there is nothing
|
|
8663
|
+
* unknowable about it. A spread-only carrier stays silent (the attribute never reaches `attrs`).
|
|
8664
|
+
* SVG elements are judged along with HTML — `accesskey` is meaningless there, which makes its
|
|
8665
|
+
* presence wrong in both namespaces.
|
|
8666
|
+
*/
|
|
8667
|
+
const a11yNoAccesskey = componentRule({
|
|
8668
|
+
id: "a11y/no-accesskey",
|
|
8669
|
+
title: "Accesskey attribute",
|
|
8670
|
+
category: "a11y",
|
|
8671
|
+
severity: "warning",
|
|
8672
|
+
label: "Accesskey attributes",
|
|
8673
|
+
recommendation: "Remove the accesskey attribute. Provide visible, focusable controls instead; if a real keyboard shortcut is needed, implement it with a key handler and document it in the page.",
|
|
8674
|
+
rationale: "The accesskey attribute assigns a page-level shortcut key, but the actual key combination varies by browser and OS, is undiscoverable to users, and routinely conflicts with screen reader and browser keyboard bindings. Long-standing accessibility guidance is to not use it.",
|
|
8675
|
+
fix: { description: "Remove the accesskey attribute." },
|
|
8676
|
+
applies: (c) => (c.elements ?? []).some((e) => e.attrs.some((a) => a.name === "accesskey")),
|
|
8677
|
+
bad: (c) => (c.elements ?? []).filter((e) => e.attrs.some((a) => a.name === "accesskey")).map((e) => ({
|
|
8678
|
+
line: e.line,
|
|
8679
|
+
message: `accesskey on <${e.tag}> — the shortcut key varies by browser and OS, is undiscoverable, and conflicts with assistive-technology bindings`
|
|
8680
|
+
}))
|
|
8681
|
+
});
|
|
8682
|
+
//#endregion
|
|
8683
|
+
//#region src/rules/a11y/no-autofocus.ts
|
|
8684
|
+
/**
|
|
8685
|
+
* `<dialog>` and popover containers run their focusing steps on show, not on page load, and both
|
|
8686
|
+
* honour an `autofocus` descendant — so autofocus there is the correct tool, not focus theft.
|
|
8687
|
+
* The popover check accepts the attribute in any form (bare, literal, expression): an expression
|
|
8688
|
+
* could resolve to a real popover value, and a generous carve-out trades a false negative for
|
|
8689
|
+
* never flagging the documented pattern (same trade as correctness/autoplay-muted's `muted`).
|
|
8690
|
+
* Neither container form counts in the SVG namespace — the focusing steps are defined for the
|
|
8691
|
+
* HTML `dialog` element and HTML elements with a `popover` attribute only; a `<dialog>` inside
|
|
8692
|
+
* `<foreignObject>` is back in HTML and still counts.
|
|
8693
|
+
*/
|
|
8694
|
+
function inShowTimeContainer(elements, start) {
|
|
8695
|
+
let e = start;
|
|
8696
|
+
while (e !== void 0) {
|
|
8697
|
+
if (!e.inSvg && (e.tag === "dialog" || e.attrs.some((a) => a.name === "popover"))) return true;
|
|
8698
|
+
e = e.parent === void 0 ? void 0 : elements[e.parent];
|
|
8699
|
+
}
|
|
8700
|
+
return false;
|
|
8701
|
+
}
|
|
8702
|
+
/**
|
|
8703
|
+
* a11y/no-autofocus — only a literal `autofocus` counts (a bare boolean attribute parses to
|
|
8704
|
+
* `value: ''`); an expression value could be `false` and passes as unknowable. The ancestor walk
|
|
8705
|
+
* uses `ElementFact.parent`, which looks through logic blocks but breaks at every non-lexical
|
|
8706
|
+
* construct (a component, `{#snippet}` body, `<svelte:element>`, custom element, `{@html}`,
|
|
8707
|
+
* `{@render}`, `<slot>`) — past a break the dialog cannot be proven, so the element is reported;
|
|
8708
|
+
* the docs page's Limitations section names these classes and the inline-suppression escape
|
|
8709
|
+
* hatch. SVG elements are judged along with HTML: SVG2 honours `autofocus` with the same
|
|
8710
|
+
* page-load focus semantics.
|
|
8711
|
+
*/
|
|
8712
|
+
const a11yNoAutofocus = componentRule({
|
|
8713
|
+
id: "a11y/no-autofocus",
|
|
8714
|
+
title: "Autofocus outside a dialog",
|
|
8715
|
+
category: "a11y",
|
|
8716
|
+
severity: "warning",
|
|
8717
|
+
label: "Autofocus placement",
|
|
8718
|
+
recommendation: "Remove the autofocus attribute and let focus start at the top of the page, or move the control into the <dialog> or popover it belongs to; focus moved in response to a user action belongs in an event handler.",
|
|
8719
|
+
rationale: "autofocus moves focus on page load without the user asking, skipping everything before the target — screen reader users lose the page context they were building, and keyboard users are dropped mid-page. Inside a <dialog> or a popover it is the correct tool (their focusing steps run on show, not on load); anywhere else it is almost always a usability bug.",
|
|
8720
|
+
fix: { description: "Remove the autofocus attribute, or move the element inside the <dialog>/popover container it is meant to focus." },
|
|
8721
|
+
applies: (c) => literalAutofocusElements(c).length > 0,
|
|
8722
|
+
bad: (c) => {
|
|
8723
|
+
const elements = c.elements ?? [];
|
|
8724
|
+
return literalAutofocusElements(c).filter((e) => !inShowTimeContainer(elements, e)).map((e) => ({
|
|
8725
|
+
line: e.line,
|
|
8726
|
+
message: `autofocus on <${e.tag}> steals focus when the page loads — keyboard and screen reader users lose their place`
|
|
8727
|
+
}));
|
|
8728
|
+
}
|
|
8729
|
+
});
|
|
8730
|
+
function literalAutofocusElements(c) {
|
|
8731
|
+
return (c.elements ?? []).filter((e) => e.attrs.some((a) => a.name === "autofocus" && a.value !== void 0));
|
|
8732
|
+
}
|
|
8733
|
+
//#endregion
|
|
8734
|
+
//#region src/rules/a11y/no-duplicate-dt.ts
|
|
8735
|
+
/**
|
|
8736
|
+
* a11y/no-duplicate-dt — the candidate gathering (direct and div-wrapped `<dt>` children of a
|
|
8737
|
+
* `<dl>`, fully-static names only, whitespace-normalized, case-sensitive) lives in the
|
|
8738
|
+
* collector; see `collectDuplicateDts` in component-parse.ts for the exemption rationale.
|
|
8739
|
+
*/
|
|
8740
|
+
const a11yNoDuplicateDt = componentRule({
|
|
8741
|
+
id: "a11y/no-duplicate-dt",
|
|
8742
|
+
title: "Duplicate <dt> names in a <dl>",
|
|
8743
|
+
category: "a11y",
|
|
8744
|
+
severity: "info",
|
|
8745
|
+
label: "Description-list names",
|
|
8746
|
+
rationale: "Within a single dl element, there should not be more than one dt element for each name — the HTML spec calls this out explicitly, and a duplicated term usually means a copy-paste error where two descriptions were meant to share one dt.",
|
|
8747
|
+
recommendation: "Merge the descriptions under a single <dt> — one dt may be followed by several <dd> — or rename the term if the entries are genuinely different.",
|
|
8748
|
+
fix: { description: "Merge the duplicated terms into one <dt> followed by both <dd> descriptions, or rename one of them." },
|
|
8749
|
+
applies: (c) => (c.duplicateDts ?? []).length > 0,
|
|
8750
|
+
bad: (c) => (c.duplicateDts ?? []).map((d) => ({
|
|
8751
|
+
line: d.line,
|
|
8752
|
+
message: `Duplicate <dt> "${d.text}" in the same <dl>`
|
|
8753
|
+
}))
|
|
8754
|
+
});
|
|
8755
|
+
//#endregion
|
|
8756
|
+
//#region src/rules/a11y/abbr-title.ts
|
|
8757
|
+
/**
|
|
8758
|
+
* a11y/abbr-title — a best-practice nudge, not a conformance check: the spec makes `title` on
|
|
8759
|
+
* `<abbr>` optional, and an expansion given in the surrounding prose is correct markup this
|
|
8760
|
+
* rule cannot see (the known false-positive class; the docs page names the inline-suppression
|
|
8761
|
+
* escape hatch for it). A blank literal `title=""` gives no expansion and is reported; an
|
|
8762
|
+
* expression value is unknowable and passes, the same predicate as a11y/accessible-name's
|
|
8763
|
+
* iframe check. SVG is skipped — `<abbr>` is not an SVG element.
|
|
8764
|
+
*/
|
|
8765
|
+
function untitledAbbrs(c) {
|
|
8766
|
+
return (c.elements ?? []).filter((e) => e.tag === "abbr" && !e.inSvg && !e.hasSpread && !e.attrs.some((a) => a.name === "title" && (a.value === void 0 || a.value.trim() !== "")));
|
|
8767
|
+
}
|
|
8768
|
+
const a11yAbbrTitle = componentRule({
|
|
8769
|
+
id: "a11y/abbr-title",
|
|
8770
|
+
title: "Abbreviation without an expansion",
|
|
8771
|
+
category: "a11y",
|
|
8772
|
+
severity: "info",
|
|
8773
|
+
label: "Abbreviation expansions",
|
|
8774
|
+
rationale: "<abbr> marks an abbreviation, but without a title giving the expansion the element adds no information — visual users get no tooltip and assistive technology has nothing to expand. The spec keeps title optional (an expansion in the surrounding text is also fine), so this is a nudge toward the common case, not a conformance requirement.",
|
|
8775
|
+
recommendation: "Add title with the expansion, or spell the term out in full at first use instead of using <abbr>. If the expansion is already given in the surrounding text, the markup is fine as written — silence the finding instead.",
|
|
8776
|
+
fix: {
|
|
8777
|
+
description: "Add a title attribute with the expansion, or spell the term out at first use.",
|
|
8778
|
+
snippet: "<abbr title=\"HyperText Markup Language\">HTML</abbr>",
|
|
8779
|
+
lang: "svelte"
|
|
8780
|
+
},
|
|
8781
|
+
applies: (c) => untitledAbbrs(c).length > 0,
|
|
8782
|
+
bad: (c) => untitledAbbrs(c).map((e) => ({
|
|
8783
|
+
line: e.line,
|
|
8784
|
+
message: "<abbr> without a title gives readers no expansion of the abbreviation"
|
|
8785
|
+
}))
|
|
8786
|
+
});
|
|
8787
|
+
//#endregion
|
|
8788
|
+
//#region src/rules/a11y/pattern-title.ts
|
|
8789
|
+
/** The input types the `pattern` attribute applies to, per the spec's applies-to table. */
|
|
8790
|
+
const PATTERN_TYPES = /* @__PURE__ */ new Set([
|
|
8791
|
+
"text",
|
|
8792
|
+
"search",
|
|
8793
|
+
"url",
|
|
8794
|
+
"tel",
|
|
8795
|
+
"email",
|
|
8796
|
+
"password"
|
|
8797
|
+
]);
|
|
8798
|
+
/**
|
|
8799
|
+
* a11y/pattern-title — only judged where `pattern` is effective: no `type` (defaults to Text)
|
|
8800
|
+
* or a literal type in the applies-to set. A literal type outside it (`type="number"`) makes
|
|
8801
|
+
* pattern inert, so requiring a title there would be wrong; an expression type or pattern is
|
|
8802
|
+
* unknowable and passes. An unknown type keyword (`type="txet"`) falls back to the Text state
|
|
8803
|
+
* where pattern does apply — skipping it is a deliberate, conservative false negative (docs
|
|
8804
|
+
* Limitations). A blank literal `title=""` describes nothing and is reported; an expression
|
|
8805
|
+
* title counts as present, the a11y/accessible-name predicate. SVG-namespace inputs never
|
|
8806
|
+
* render as form controls and are skipped.
|
|
8807
|
+
*/
|
|
8808
|
+
function patternInputsMissingTitle(c) {
|
|
8809
|
+
return (c.elements ?? []).filter((e) => {
|
|
8810
|
+
if (e.tag !== "input" || e.inSvg || e.hasSpread) return false;
|
|
8811
|
+
const pattern = e.attrs.find((a) => a.name === "pattern");
|
|
8812
|
+
if (pattern === void 0 || pattern.value === void 0) return false;
|
|
8813
|
+
const type = e.attrs.find((a) => a.name === "type");
|
|
8814
|
+
if (type !== void 0 && (type.value === void 0 || !PATTERN_TYPES.has(type.value.trim().toLowerCase()))) return false;
|
|
8815
|
+
return !e.attrs.some((a) => a.name === "title" && (a.value === void 0 || a.value.trim() !== ""));
|
|
8816
|
+
});
|
|
8817
|
+
}
|
|
8818
|
+
const a11yPatternTitle = componentRule({
|
|
8819
|
+
id: "a11y/pattern-title",
|
|
8820
|
+
title: "Pattern input without a format description",
|
|
8821
|
+
category: "a11y",
|
|
8822
|
+
severity: "info",
|
|
8823
|
+
label: "Pattern descriptions",
|
|
8824
|
+
rationale: "When an <input> has a pattern attribute, the spec says authors should include a title describing the expected format — browsers surface it in the validation error, and without it a failed submit tells the user only that the value is wrong, not what right looks like.",
|
|
8825
|
+
recommendation: "Add a title describing the expected format in plain words (e.g. \"Letters, a dash, then digits\"), and mirror it in visible help text — title alone is unavailable to many users.",
|
|
8826
|
+
fix: {
|
|
8827
|
+
description: "Add a title attribute describing the expected format.",
|
|
8828
|
+
snippet: "<input pattern=\"[A-Za-z]+-[0-9]+\" title=\"Letters, a dash, then digits\" />",
|
|
8829
|
+
lang: "svelte"
|
|
8830
|
+
},
|
|
8831
|
+
applies: (c) => patternInputsMissingTitle(c).length > 0,
|
|
8832
|
+
bad: (c) => patternInputsMissingTitle(c).map((e) => ({
|
|
8833
|
+
line: e.line,
|
|
8834
|
+
message: "<input pattern> without a title — a failed match tells the user nothing about the expected format"
|
|
8835
|
+
}))
|
|
8836
|
+
});
|
|
8837
|
+
//#endregion
|
|
8409
8838
|
//#region src/html-spec/index.ts
|
|
8410
8839
|
/** The HTML element's spec row; `undefined` for SVG (`svg:*`) and unknown names. */
|
|
8411
8840
|
function htmlElement(tag) {
|
|
@@ -9396,6 +9825,7 @@ const allRules = [
|
|
|
9396
9825
|
correctnessStalePropDerivation,
|
|
9397
9826
|
correctnessNonreactiveBuiltinState,
|
|
9398
9827
|
correctnessCheckableBindValue,
|
|
9828
|
+
correctnessAutoplayMuted,
|
|
9399
9829
|
correctnessOrphanEffect,
|
|
9400
9830
|
correctnessOrphanLifecycle,
|
|
9401
9831
|
correctnessBasePathNavigation,
|
|
@@ -9421,6 +9851,7 @@ const allRules = [
|
|
|
9421
9851
|
performanceLoadWaterfall,
|
|
9422
9852
|
performanceSequentialAwaits,
|
|
9423
9853
|
performanceStateRaw,
|
|
9854
|
+
performanceIframeLoading,
|
|
9424
9855
|
a11yInvalidRole,
|
|
9425
9856
|
a11yUnknownAriaAttribute,
|
|
9426
9857
|
a11yRequiredAriaProps,
|
|
@@ -9431,6 +9862,12 @@ const allRules = [
|
|
|
9431
9862
|
a11yUseList,
|
|
9432
9863
|
a11yPlaceholderLabelOption,
|
|
9433
9864
|
a11yRequireDatetime,
|
|
9865
|
+
a11yPositiveTabindex,
|
|
9866
|
+
a11yNoAccesskey,
|
|
9867
|
+
a11yNoAutofocus,
|
|
9868
|
+
a11yNoDuplicateDt,
|
|
9869
|
+
a11yAbbrTitle,
|
|
9870
|
+
a11yPatternTitle,
|
|
9434
9871
|
a11yDeprecatedElement,
|
|
9435
9872
|
a11yDeprecatedAttr,
|
|
9436
9873
|
a11yDisallowedAriaProps,
|
|
@@ -9892,4 +10329,4 @@ function formatMarkdownReport(results, config, meta) {
|
|
|
9892
10329
|
return lines.join("\n");
|
|
9893
10330
|
}
|
|
9894
10331
|
//#endregion
|
|
9895
|
-
export {
|
|
10332
|
+
export { architectureRouteComponentImport as $, seoOgTitle as $n, correctnessEachKey as $t, a11yAbbrTitle as A, validateRuleSetting as An, textFromNodes as At, a11yInteractiveNesting as B, performanceRenderBlockingScript as Bn, correctnessServerBrowserGlobal as Bt, a11yPermittedContents as C, intOption as Cn, attrText as Ct, a11yDeprecatedAttr as D, resolveRuleOptions as Dn, findAttr as Dt, a11yDisallowedAriaProps as E, mapOption as En, attrValueOf as Et, a11yRequireDatetime as F, overrideMatches as Fn, securityServerModuleState as Ft, performanceIframeLoading as G, performanceResponsiveImage as Gn, correctnessCheckableBindValue as Gt, a11yRequiredAriaProps as H, performanceFontPreloadCrossorigin as Hn, correctnessOrphanLifecycle as Ht, a11yPlaceholderLabelOption as I, selectRules as In, securityHandlerStateWrite as It, performanceLoadWaterfall as J, imageRule as Jn, correctnessPropMutation as Jt, performanceStateRaw as K, performanceImageLoadingHint as Kn, correctnessNonreactiveBuiltinState as Kt, a11yUseList as L, settingOptions as Ln, securityJavascriptUrl as Lt, a11yNoAutofocus as M, applyRuleSeverities as Mn, architecturePropCount as Mt, a11yNoAccesskey as N, compileOverrides as Nn, architectureComponentSize as Nt, a11yDeprecatedElement as O, shouldSkipRangeCheck as On, lineOf as Ot, a11yPositiveTabindex as P, formatFailedRuleWarning as Pn, securitySharedStateImport as Pt, architectureDocLinkTarget as Q, seoJsonLd as Qn, correctnessEachIndexKey as Qt, a11yLabelHasControl as R, settingSeverity as Rn, securityRawHtml as Rt, a11yRequiredElement as S, performancePreconnect as Sn, CHILD_NODE_KEYS as St, a11yDeprecatedAria as T, listOption as Tn, attrValue as Tt, a11yUnknownAriaAttribute as U, performancePreloadMissingAs as Un, correctnessOrphanEffect as Ut, a11yInvalidAriaValue as V, performanceLcpImage as Vn, correctnessBasePathNavigation as Vt, a11yInvalidRole as W, linkRule as Wn, correctnessAutoplayMuted as Wt, performanceNamespaceImport as X, seoSitemapXml as Xn, correctnessEffectAsOnMount as Xt, performanceMinifyDisabled as Y, seoHtmlLang as Yn, correctnessUnmutatedState as Yt, performanceHeavyImport as Z, seoRobotsTxt as Zn, correctnessEffectAsDerived as Zt, a11yNoMissingIdRef as _, seoViewport as _n, decodeFragmentId as _t, formatJsonReport as a, seoHreflang as an, classify as ar, collectTopLevelBindings as at, a11yDuplicateLandmark as b, seoTwitterCard as bn, splitTokens as bt, scoresByCategory as c, seoDescriptionLength as cn, summarize as cr, resolveRepoLocalPath as ct, messageText as d, seoJsonLdPlaceholder as dn, CATEGORIES as dr, collectSuppressions as dt, seoSsrDisabled as en, seoOgImage as er, architectureReservedNamePlacement as et, ruleMetaById as f, seoJsonLdDateFormat as fn, defaultConfig as fr, parseComponentFacts as ft, a11yUnverifiedIdRef as g, seoSitemapInRobots as gn, LANDMARK_ROLES as gt, explainRule as h, seoJsonLdValidity as hn, IDREF_ATTRS as ht, buildJsonReport as i, seoSingleH1 as in, seoTitlePresence as ir, architecturePrivateScopeImport as it, a11yNoDuplicateDt as j, applyOverrides as jn, valueFromNodes as jt, a11yPatternTitle as k, validateRuleOptions as kn, parseSvelte as kt, formatGithubReport as l, seoTitleLength as ln, docsUrlFor as lr, resolveRunesModuleSpecifier as lt, allRules as m, seoJsonLdDeprecatedType as mn, defineConfig as mr, unwrapTs as mt, mdEscape as n, seoDuplicateDescription as nn, seoDescriptionPresence as nr, architectureDirectoryNaming as nt, computeHealth as o, seoImageAlt as on, effectiveSeverity as or, parseInMemoryExports as ot, severityToSarifLevel as p, seoJsonLdRelativeUrl as pn, defaultProject as pr, parseModuleProgram as pt, performanceSequentialAwaits as q, performanceImageDimensions as qn, correctnessStalePropDerivation as qt, terminalSafe as r, seoDuplicateTitle as rn, headTagRule as rr, architectureUnitEntryFile as rt, computeScore as s, seoCharset as sn, hasFailureAtOrAbove as sr, parseKitModuleFacts as st, formatMarkdownReport as t, seoHeadingLevelSkip as tn, seoCanonicalUrl as tr, architectureReservedDirectoryNames as tt, SEVERITY_RANK as u, seoJsonLdRequiredProps as un, isPenalized as ur, collectNamedImportAliases as ut, a11yIdDuplication as v, seoOgUrl as vn, foldOccurrences as vt, a11yDisallowedElement as w, isMentionedAnywhere as wn, attrTextOf as wt, a11yDoctype as x, seoIndexability as xn, stripTextDirective as xt, a11yTopLevelLandmark as y, seoOgDescription as yn, isTopFragment as yt, a11yAccessibleName as z, withFailedRulesOff as zn, correctnessInstanceBrowserGlobal as zt };
|