From 6188372b6168557d59146255db9ebef6cbafbf93 Mon Sep 17 00:00:00 2001 From: Luke LaValva Date: Wed, 22 Jul 2026 18:26:10 +0000 Subject: [PATCH 01/19] Make the a11y linter aware of child templates The axe-core HTML plugin previously replaced every custom tag with a bare
wrapper, so it could never see what a child template renders and any component usage suppressed most content rules on its ancestors (via the unknownBody exception). Extraction now inlines the extracted skeleton of resolvable child templates at the usage site, guarded so a diagnostic is only ever produced when it is provably true for rendered output: - Child skeletons are resolved through the taglib lookup, cached per template and project version, and bounded by cycle detection, a depth limit, and a byte budget; anything unresolvable falls back to the old
behavior. - A template's default body slot (`<${input.renderBody}/>`) is detected so usage-site body content is spliced into the child's true position; templates without a single known slot keep the previous behavior. - Any uncertainty inside a child (unescaped placeholders, unresolvable tags, dynamic body content) propagates to the usage site so existing unknownBody suppression still applies. - Diagnostics on a child skeleton's top-level elements are re-anchored to the tag name at the usage site; deeper elements are left to the child template's own validation. - Control flow tags (if/else/for/while) emit their body directly instead of a fabricated
, so structural rules see real parent/child relationships. - listitem and dlitem are un-blacklisted, gated by a new requiresKnownParent exception that mirrors exactly the ancestor chain axe consults (including dlitem's presentational-div lookthrough), and never fires for elements whose rendered parent is unknown. - Duplicate-detection rules (accesskeys, landmark-unique, ...) are suppressed for elements in control flow branches, removing a class of pre-existing false positives from mutually exclusive branches. No diagnostics changed on the existing fixture corpus; new fixtures cover inlined context, slot splicing, uncertainty suppression, recursion, control flow, and dlitem gating. --- .changeset/spotty-moons-repeat.md | 11 + agent-feedback/bugs.md | 12 + cspell.json | 2 + .../components/fancy-item.html | 1 + .../components/fancy-item.md | 0 .../components/fancy-item.ts | 61 ++++ .../index.html | 5 + .../child-template-context.expected/index.md | 12 + .../child-template-context.expected/index.ts | 75 +++++ .../components/fancy-item.marko | 1 + .../html/child-template-context/index.marko | 7 + .../components/icon-arrow.html | 1 + .../components/icon-arrow.md | 0 .../components/icon-arrow.ts | 62 +++++ .../components/text-label.html | 1 + .../components/text-label.md | 0 .../components/text-label.ts | 61 ++++ .../index.html | 5 + .../index.md | 17 ++ .../index.ts | 77 +++++ .../components/icon-arrow.marko | 1 + .../components/text-label.marko | 1 + .../html/child-template-link-name/index.marko | 7 + .../components/tree-item.html | 6 + .../components/tree-item.md | 0 .../components/tree-item.ts | 72 +++++ .../index.html | 8 + .../index.md | 0 .../index.ts | 64 +++++ .../components/tree-item.marko | 6 + .../html/child-template-recursive/index.marko | 3 + .../components/my-card.html | 1 + .../components/my-card.md | 0 .../components/my-card.ts | 66 +++++ .../components/my-list.html | 1 + .../components/my-list.md | 0 .../components/my-list.ts | 66 +++++ .../child-template-slot.expected/index.html | 6 + .../child-template-slot.expected/index.md | 12 + .../child-template-slot.expected/index.ts | 94 +++++++ .../components/my-card.marko | 5 + .../components/my-list.marko | 5 + .../html/child-template-slot/index.marko | 8 + .../components/raw-html.html | 1 + .../components/raw-html.md | 0 .../components/raw-html.ts | 64 +++++ .../index.html | 3 + .../index.md | 11 + .../index.ts | 64 +++++ .../components/raw-html.marko | 5 + .../html/child-template-uncertain/index.marko | 3 + .../index.html | 15 + .../index.md | 13 + .../index.ts | 116 ++++++++ .../html/control-flow-conditional/index.marko | 25 ++ .../dlitem-context.expected/index.html | 8 + .../dlitem-context.expected/index.md | 12 + .../dlitem-context.expected/index.ts | 95 +++++++ .../fixtures/html/dlitem-context/index.marko | 10 + .../index.html | 2 +- .../index.html | 2 +- .../index.html | 8 +- .../tags/my-menu/index.html | 3 +- .../attr-tags-dynamic-for.expected/index.html | 44 +-- .../attr-tags-dynamic-if.expected/index.html | 64 ++--- .../index.html | 16 +- .../index.html | 20 +- .../index.html | 8 +- .../attr-tags-generic.expected/index.html | 4 +- .../index.html | 8 +- .../tags/my-layout/index.html | 7 +- .../attr-tags-nested-type.expected/index.html | 4 +- .../index.html | 6 +- .../bound-attr-mut-ident.expected/index.html | 2 +- .../class-api-basic.expected/index.html | 20 +- .../custom-tag-args.expected/index.html | 1 + .../el-ref-hoist.expected/index.html | 2 +- .../for-tag-attr-tags.expected/index.html | 8 +- .../for-tag-input.expected/index.html | 20 +- .../__snapshots__/for-tag.expected/index.html | 37 ++- .../__snapshots__/if-tag.expected/index.html | 59 ++-- .../index.html | 6 +- .../recursive-define-tag.expected/index.html | 7 +- .../render-body-basic.expected/index.html | 3 +- .../return-tag-basic.expected/index.html | 2 +- .../return-tag-nested.expected/index.html | 12 +- .../simple-hoist.expected/index.html | 2 +- .../tag-param-mutation.expected/index.html | 10 +- .../index.html | 12 +- .../index.html | 5 +- .../index.html | 2 +- .../index.html | 4 +- .../tag-var-mutations.expected/index.html | 2 +- .../tags-api-basic.expected/index.html | 24 +- .../while-tag.expected/index.html | 11 +- .../service/html/axe-rules/rule-exceptions.ts | 52 +++- .../language-server/src/service/html/index.ts | 263 ++++++++++++++++-- .../src/extractors/html/index.ts | 257 ++++++++++++++--- packages/language-tools/src/util/extractor.ts | 5 + 99 files changed, 2020 insertions(+), 292 deletions(-) create mode 100644 .changeset/spotty-moons-repeat.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/components/fancy-item.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/components/fancy-item.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/components/fancy-item.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/__snapshots__/child-template-context.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/components/fancy-item.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-context/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/icon-arrow.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/icon-arrow.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/icon-arrow.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/text-label.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/text-label.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/components/text-label.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/__snapshots__/child-template-link-name.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/components/icon-arrow.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/components/text-label.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-link-name/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/components/tree-item.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/components/tree-item.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/components/tree-item.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/__snapshots__/child-template-recursive.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/components/tree-item.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-recursive/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-card.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-card.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-card.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-list.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-list.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/components/my-list.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/__snapshots__/child-template-slot.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/components/my-card.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/components/my-list.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-slot/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/components/raw-html.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/components/raw-html.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/components/raw-html.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/__snapshots__/child-template-uncertain.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/components/raw-html.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/child-template-uncertain/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/control-flow-conditional/__snapshots__/control-flow-conditional.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/control-flow-conditional/__snapshots__/control-flow-conditional.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/control-flow-conditional/__snapshots__/control-flow-conditional.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/control-flow-conditional/index.marko create mode 100644 packages/language-server/src/__tests__/fixtures/html/dlitem-context/__snapshots__/dlitem-context.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/html/dlitem-context/__snapshots__/dlitem-context.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/html/dlitem-context/__snapshots__/dlitem-context.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/html/dlitem-context/index.marko diff --git a/.changeset/spotty-moons-repeat.md b/.changeset/spotty-moons-repeat.md new file mode 100644 index 00000000..9e74a5fb --- /dev/null +++ b/.changeset/spotty-moons-repeat.md @@ -0,0 +1,11 @@ +--- +"@marko/language-tools": minor +"@marko/language-server": minor +--- + +Improve the axe-core accessibility linter's awareness of child templates: + +- Custom tags that resolve to a `.marko` template now inline that template's extracted HTML at the usage site (with cycle, depth, and size guards), so rules can evaluate a tag's rendered output in context — eg `` now reports `link-name` when the icon renders `aria-hidden` content, and diagnostics for a template's top-level elements are re-anchored to the tag usage (`` inside `
` reports `listitem`). +- Control flow tags (``, ``, ``, ``) no longer fabricate `
` wrappers around their body, so structural rules see true parent/child relationships. +- The `listitem` and `dlitem` rules are now enabled, gated so they only report when the parent chain axe consults is fully known (never for a template's own top-level elements, whose parent comes from the usage site). +- Duplicate-detection rules (`accesskeys`, `landmark-unique`, `identical-links-same-purpose`, ...) are suppressed for elements inside control flow branches, since only one branch renders at a time. diff --git a/agent-feedback/bugs.md b/agent-feedback/bugs.md index bb895850..fd6a8167 100644 --- a/agent-feedback/bugs.md +++ b/agent-feedback/bugs.md @@ -23,3 +23,15 @@ The runtime accepts a string OR number loop key — `assertValidLoopKey` throws `packages/language-tools/src/extractors/script/index.ts` › `#writeTag` | 2026-07-19 | impact:high | effort:med A custom tag that fails to resolve (a component referenced by kebab tag name with no `import`, or a typo'd tag name) is a hard build error but produces ZERO diagnostics under `mtc` — the type-check tool agents are told to run. Reproduced in a real @marko/run scaffold: Marko 6 auto-discovers only `tags/` dirs (`runtime-tags` sets `tagDiscoveryDirs = ["tags"]` at `packages/runtime-tags/src/translator/index.ts:40`), so `src/components/*.marko` is NOT auto-registered — `` (no import) and the typo `` both pass `mtc` with exit 0, while `npm run compile -o html` / `marko-run build` throw `Unable to find entry point for custom tag ` (`resolveTagImport` at `tags.js:353`; `tagNotFoundError` at `packages/runtime-tags/src/translator/visitors/tag/custom-tag.ts:411`). The identical wrong attr on a RESOLVED tag correctly errors TS2322 (verified against ``), proving tag resolution — not the attribute — is the gate: `#writeTag` lowers an unresolvable tag to `renderDynamicTag(...)` whose input is `Record`, so every attribute and callback param goes unchecked. This is the worst shape for an agent whose deterministic verify loop is `mtc`: it creates a component, references it by tag (natural for anyone used to auto-registering `components/` dirs), sees a clean type-check, and ships wrong props or a misspelled tag; the build then fails with an "entry point" error that looks unrelated to the type loop. Direction: have @marko/language-tools emit a distinct "cannot resolve tag ``" diagnostic mirroring the compiler instead of degrading to an untyped dynamic tag. The compiler-side authority is marko-js/marko's `packages/compiler/src/babel-utils/tags.js` › `resolveTagImport` and `custom-tag.ts` › `tagNotFoundError`. Distinct from the run dx.md route-types entries (missing `Run` global / stale `routes.d.ts`), which concern generated route types, not custom-tag resolution. + +## Propagate descendant dynamic attribute values into `hasDynamicBody` for a11y rule suppression + +`packages/language-tools/src/extractors/html/index.ts` › `HTMLExtractor.#visitNode` | 2026-07-22 | impact:med | effort:med + +Dynamic _attribute_ values on descendants do not propagate into an ancestor's `hasDynamicBody`, so `unknownBody` rule exceptions can miss. Example: `
` extracts the child as `role="dynamic"` (an invalid role) while the outer element's `hasDynamicBody` stays false, so axe's `aria-required-children` reports a violation even though the runtime role could be `listitem` — a false positive that predates child-template inlining. `#visitNode` returns `isDynamic || hasDynamicBody` and deliberately ignores `hasDynamicAttrs`; a fix could propagate a separate "semantics-affecting dynamic attr" flag (role/aria-\*/tabindex only, to avoid over-suppressing on eg dynamic `class`) into ancestors. Re-verify: `
` in an html fixture produces an `aria-required-children` diagnostic. + +## Suppress duplicate-detection a11y rules when the duplicate _counterpart_ is conditional, via axe relatedNodes + +`packages/language-server/src/service/html/index.ts` › `doValidate` | 2026-07-22 | impact:low | effort:med + +The `conditionalContent` exception only suppresses duplicate-detection rules (accesskeys, landmark-unique, identical-links-same-purpose, ...) when the _violating_ element is inside a control flow branch. When the violating element is unconditional but its duplicate counterpart is conditional (eg a static `
-
+ placeholder @@ -12,5 +12,5 @@ placeholder -
+
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.html index e7c0b20f..76b4b201 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.html @@ -1,16 +1,16 @@
-
+ -
placeholder placeholder
+
placeholder placeholder
-
+
-
placeholder
+
placeholder
diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/tags/my-layout/index.html b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/tags/my-layout/index.html index 7e3c33c3..fc25e535 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/tags/my-layout/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/tags/my-layout/index.html @@ -1,5 +1,4 @@ -
-
+ + -
-
\ No newline at end of file + diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.html index d84d1a16..ef0af866 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.html @@ -1,8 +1,8 @@
-
+ -
+
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.html index 7aba471a..aebed217 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.html @@ -1,9 +1,9 @@
... -
+ ... -
-
placeholder
+ + placeholder
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-ident/__snapshots__/bound-attr-mut-ident.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-ident/__snapshots__/bound-attr-mut-ident.expected/index.html index d9fbdcc1..6f0c9f31 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-ident/__snapshots__/bound-attr-mut-ident.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-ident/__snapshots__/bound-attr-mut-ident.expected/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-basic/__snapshots__/class-api-basic.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/class-api-basic/__snapshots__/class-api-basic.expected/index.html index 8861aef5..0f5056d0 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-basic/__snapshots__/class-api-basic.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-basic/__snapshots__/class-api-basic.expected/index.html @@ -1,23 +1,23 @@

Year: placeholder

-
- isSmartPhone: placeholder -
+ + isSmartPhone: placeholder + - phoneType: placeholder + phoneType: placeholder -
- placeholder placeholder placeholder -
+ + placeholder placeholder placeholder + -
+
placeholder
-
Hello placeholder
-
Placeholder example: ${someValue}
+
Hello placeholder
+
Placeholder example: ${someValue}
diff --git a/packages/language-server/src/__tests__/fixtures/script/custom-tag-args/__snapshots__/custom-tag-args.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/custom-tag-args/__snapshots__/custom-tag-args.expected/index.html index e69de29b..acda27bc 100644 --- a/packages/language-server/src/__tests__/fixtures/script/custom-tag-args/__snapshots__/custom-tag-args.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/custom-tag-args/__snapshots__/custom-tag-args.expected/index.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.html index 6a018112..3143e0dc 100644 --- a/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/for-tag-attr-tags/__snapshots__/for-tag-attr-tags.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/for-tag-attr-tags/__snapshots__/for-tag-attr-tags.expected/index.html index 4ecd3f89..4575e0e3 100644 --- a/packages/language-server/src/__tests__/fixtures/script/for-tag-attr-tags/__snapshots__/for-tag-attr-tags.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/for-tag-attr-tags/__snapshots__/for-tag-attr-tags.expected/index.html @@ -1,19 +1,19 @@
-
+ placeholder placeholder -
+
-
+ placeholder placeholder -
+
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.html index 9b65e337..f6527a8f 100644 --- a/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.html @@ -1,30 +1,30 @@ - + - -
+
-
+ placeholder -
-
+ + placeholder -
+
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.html index 9b7da9ff..78bd7a98 100644 --- a/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.html @@ -1,45 +1,44 @@ -
+ Repeated! -
+ placeholder placeholder placeholder -
-
+ + -
+ placeholder placeholder -
-
+ + -
+ placeholder -
-
+ + placeholder -
+ placeholder -
+ -
+ placeholder -
-
+ + placeholder -
+ placeholder -
+ -
+ Should error -
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/if-tag/__snapshots__/if-tag.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/if-tag/__snapshots__/if-tag.expected/index.html index dcae09d7..a6d81858 100644 --- a/packages/language-server/src/__tests__/fixtures/script/if-tag/__snapshots__/if-tag.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/if-tag/__snapshots__/if-tag.expected/index.html @@ -1,53 +1,52 @@ -
+ Hi -
+ Hi -
+ Hi -
-
+
+ Hello -
+ World -
+
Hi -
+ Goodbye -
+ + + + + -
+ -
+ -
+ -
+ -
+ + + -
-
-
-
-
-
-
-
+ + -
-
+ + Hi -
+ Hi -
+ Hi -
+ Hi -
+ Hi -
+ Hi -
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/prefer-local-identifier-tag-name/__snapshots__/prefer-local-identifier-tag-name.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/prefer-local-identifier-tag-name/__snapshots__/prefer-local-identifier-tag-name.expected/index.html index 3c0e3fb6..3e1911be 100644 --- a/packages/language-server/src/__tests__/fixtures/script/prefer-local-identifier-tag-name/__snapshots__/prefer-local-identifier-tag-name.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/prefer-local-identifier-tag-name/__snapshots__/prefer-local-identifier-tag-name.expected/index.html @@ -1,4 +1,4 @@ -
+
- -
\ No newline at end of file +
+
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.html index 024dc4b2..06ef31bc 100644 --- a/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.html @@ -1,10 +1,9 @@
-
+ -
-
+ +
-
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/render-body-basic/__snapshots__/render-body-basic.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/render-body-basic/__snapshots__/render-body-basic.expected/index.html index 1db6f2e9..ecd622fb 100644 --- a/packages/language-server/src/__tests__/fixtures/script/render-body-basic/__snapshots__/render-body-basic.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/render-body-basic/__snapshots__/render-body-basic.expected/index.html @@ -1,4 +1,3 @@ -
+ Hi placeholder -
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/return-tag-basic/__snapshots__/return-tag-basic.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/return-tag-basic/__snapshots__/return-tag-basic.expected/index.html index 94500271..04b617c5 100644 --- a/packages/language-server/src/__tests__/fixtures/script/return-tag-basic/__snapshots__/return-tag-basic.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/return-tag-basic/__snapshots__/return-tag-basic.expected/index.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.html index ab956d9a..7b5869b6 100644 --- a/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.html @@ -1,13 +1,13 @@ -
+ placeholder -
+ -
+ -
+ -
+ -
placeholder \ No newline at end of file +placeholder \ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/simple-hoist/__snapshots__/simple-hoist.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/simple-hoist/__snapshots__/simple-hoist.expected/index.html index 6386858d..a8ff6359 100644 --- a/packages/language-server/src/__tests__/fixtures/script/simple-hoist/__snapshots__/simple-hoist.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/simple-hoist/__snapshots__/simple-hoist.expected/index.html @@ -1,7 +1,7 @@
placeholder -
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-param-mutation/__snapshots__/tag-param-mutation.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-param-mutation/__snapshots__/tag-param-mutation.expected/index.html index 79fb6b87..df4df09d 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-param-mutation/__snapshots__/tag-param-mutation.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-param-mutation/__snapshots__/tag-param-mutation.expected/index.html @@ -1,9 +1,9 @@ -
- -
+ - -
\ No newline at end of file + + \ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.html index d77dcb1c..0bda8f14 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.html @@ -1,15 +1,15 @@ -
+ -
+ placeholder -
-
-
+ + + -
placeholder \ No newline at end of file +placeholder \ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-patterns/__snapshots__/tag-var-mutation-patterns.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-patterns/__snapshots__/tag-var-mutation-patterns.expected/index.html index ede28a2a..a51f747c 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-patterns/__snapshots__/tag-var-mutation-patterns.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-patterns/__snapshots__/tag-var-mutation-patterns.expected/index.html @@ -1,4 +1,3 @@ -
+ - -
\ No newline at end of file + diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-shadowing/__snapshots__/tag-var-mutation-shadowing.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-shadowing/__snapshots__/tag-var-mutation-shadowing.expected/index.html index fb0a7db5..6f0c9f31 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-shadowing/__snapshots__/tag-var-mutation-shadowing.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutation-shadowing/__snapshots__/tag-var-mutation-shadowing.expected/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations-nested/__snapshots__/tag-var-mutations-nested.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations-nested/__snapshots__/tag-var-mutations-nested.expected/index.html index 2df50131..f046ad6f 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations-nested/__snapshots__/tag-var-mutations-nested.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations-nested/__snapshots__/tag-var-mutations-nested.expected/index.html @@ -1,4 +1,4 @@ -
+
- +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations/__snapshots__/tag-var-mutations.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations/__snapshots__/tag-var-mutations.expected/index.html index 408f5eeb..2be7abbe 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations/__snapshots__/tag-var-mutations.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-mutations/__snapshots__/tag-var-mutations.expected/index.html @@ -1 +1 @@ -
\ No newline at end of file +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.html index 6b2284f8..512c3fde 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.html @@ -1,22 +1,22 @@ -
-

Year: placeholder

+
+

Year: placeholder

-
- isSmartPhone: placeholder -
+ + isSmartPhone: placeholder + - phoneType: placeholder + phoneType: placeholder -
+ - placeholder placeholder placeholder + placeholder placeholder placeholder -
+ -
+
placeholder
-
Hello placeholder
-
Placeholder example: ${someValue}
+
Hello placeholder
+
Placeholder example: ${someValue}
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.html index 91d9e2a5..15c9fc06 100644 --- a/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.html +++ b/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.html @@ -1,14 +1,13 @@ -
+ placeholder -
+ placeholder -
+ -
-
+ + No Condition. -
\ No newline at end of file diff --git a/packages/language-server/src/service/html/axe-rules/rule-exceptions.ts b/packages/language-server/src/service/html/axe-rules/rule-exceptions.ts index c1a5ae2a..6e372052 100644 --- a/packages/language-server/src/service/html/axe-rules/rule-exceptions.ts +++ b/packages/language-server/src/service/html/axe-rules/rule-exceptions.ts @@ -21,12 +21,35 @@ export interface Exceptions { * Exclude if the body content can't be determined */ unknownBody?: boolean; + /** + * Exclude if the element is inside a control flow branch (``, ``, + * ...). For rules that flag duplicated content across the document: + * branches are mutually exclusive at runtime, so apparent duplicates may + * never coexist in rendered output. + */ + conditionalContent?: boolean; + /** + * Only report when the parent chain axe consults for the rule is fully + * known. This suppresses elements whose rendered parent comes from a usage + * site we can't see (top-level elements of a template) and elements under + * fabricated `
` placeholders. `"div-wrapped"` also requires a known + * grandparent when the direct parent is a presentational `
` (matching + * axe's `dlitem` semantics). + */ + requiresKnownParent?: true | "div-wrapped"; } type Blacklist = // Explicitly blacklisted for Marko Language Server | typeof r.structure.frameTested - // Requires a parent component to validate; we can potentially add support with child component analysis + // Requires context that may live in an *ancestor* template we can't see. + // `aria-required-parent` passes if any ancestor supplies the required role, + // so a component whose usage sites provide that ancestor would always be a + // false positive. Labels/names can be associated from the usage site (eg a + // wrapping `