diff --git a/.changeset/spotty-moons-repeat.md b/.changeset/spotty-moons-repeat.md new file mode 100644 index 00000000..0a17d6d1 --- /dev/null +++ b/.changeset/spotty-moons-repeat.md @@ -0,0 +1,6 @@ +--- +"@marko/language-tools": minor +"@marko/language-server": minor +--- + +Take child templates into account in the accessibility linter, and enable page-scoped rules for fully static documents. 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 `