Skip to content

feat: add customNamespaces option for custom namespaced attributes#3057

Closed
khromov wants to merge 1 commit into
sveltejs:masterfrom
khromov:feat/custom-namespaces
Closed

feat: add customNamespaces option for custom namespaced attributes#3057
khromov wants to merge 1 commit into
sveltejs:masterfrom
khromov:feat/custom-namespaces

Conversation

@khromov

@khromov khromov commented Jun 14, 2026

Copy link
Copy Markdown

Problem

Frameworks built on top of Svelte attach custom namespaced attributes to elements and components to carry framework-level metadata, e.g. an SSR/islands framework:

<Counter mochi:hydrate />

At runtime Svelte passes these through, but at type-check time svelte2tsx emits the attribute as a real prop/attribute, so TypeScript reports a hard error:

Type 'true' is not assignable to type 'never'.

There was no supported way to exempt these attributes. Downstream projects monkey-patch the bundled svelte-check output (a hard-coded carve-out in handleAttribute), which breaks on every version bump, hard-codes a single prefix, and only covers the component branch.

Solution

An opt-in, declarative customNamespaces option, configured once in svelte.config.js and honored by both the CLI (svelte-check) and the editor (language server):

// svelte.config.js
export default {
  customNamespaces: ['mochi']
};

An entry 'mochi' matches an attribute named exactly mochi or any attribute starting with mochi: (matching rule: name === ns || name.startsWith(ns + ':'); a trailing : is normalized away). Matching attributes reuse the existing __sveltets_2_empty({ ... }) wrapper that data-* already uses, in both the element (addAttribute) and component (addProp) branches — so the attribute name is exempt from prop/attribute validation while any value expression stays type-checked (mochi:defer={someVar} still errors if someVar is undefined).

Why this shape

  • Opt-in, not automatic — treating all unknown colon-namespaced attributes as opaque would silently hide typos and conflict with Svelte's directive semantics. Type safety stays the default.
  • Namespace list, not a predicate — declarative string[] mirrors the existing data-/-- handling, is trivial to serialize/validate/document, and works everywhere config is consumed. A predicate could be added later as a superset.
  • customNamespaces over a generic prefix match — real-world cases are colon-delimited namespaces; "namespace" matches Svelte's own vocabulary (bind:, on:, use:).
  • Config in svelte.config.js — one source of truth keeps CLI and editor in lockstep; a CLI-only fix would still leave red squiggles in the IDE.

Changes

  • svelte2tsx: new customNamespaces?: string[] option (normalized), threaded svelte2tsx → convertHtmlxToJsx → handleAttribute; matching branch added to both attribute paths.
  • svelte-language-server: reads customNamespaces from svelte.config.js (SvelteConfig) and passes it through the document snapshot path.
  • svelte-check: reads it from the loaded config in the incremental emit path. (The non-incremental path already flows through the language server.)

Tests

  • svelte2tsx fixtures: custom-namespaces (with option; covers component + element, value-less, expression value, and bare-namespace match) and custom-namespaces-disabled (control — confirms unwrapped emit, i.e. no global behavior change).
  • language-server diagnostics fixtures: enabled → 0 errors; value-checked → only Cannot find name 'typoVar' (proves expressions still checked); disabled → exactly Type 'true' is not assignable to type 'never'.

Docs / changeset

  • svelte-check README: new "Custom attribute namespaces" section, cross-linking --compiler-warnings "attribute_illegal_colon:ignore" (frameworks typically need both: the warning suppressed and the namespace registered).
  • minor changeset for svelte2tsx, svelte-check, svelte-language-server.

Notes / open questions for maintainers

  • CLI flag: a --custom-namespaces mochi,foo convenience flag was intentionally left out (config-first gives editor parity for free; threading a CLI value into the per-file config loader is invasive). Happy to add it as a follow-up if desired.
  • Prefix vs namespace: scoped to the namespace shape; a generic prefix match is broader than needed. Open to changing if maintainers prefer.

Out of scope

  • Suppressing the compiler attribute_illegal_colon warning (already configurable via --compiler-warnings).
  • Changing Svelte core to register known namespaces.

…m type validation

Frameworks built on top of Svelte attach custom namespaced attributes
(e.g. <Counter mochi:hydrate />) which previously caused
"Type 'true' is not assignable to type 'never'" errors.

Adds a configurable, opt-in customNamespaces option:
- svelte2tsx: new customNamespaces?: string[] option, honored in both the
  element (addAttribute) and component (addProp) branches via the existing
  __sveltets_2_empty wrapper so value expressions stay type-checked.
- svelte-check (CLI) and language server (editor) both read it from
  svelte.config.js, keeping CLI and editor in lockstep.

Includes svelte2tsx fixtures, language-server diagnostics tests, README
docs, and a changeset.
@changeset-bot

changeset-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d77082b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
svelte2tsx Minor
svelte-check Minor
svelte-language-server Minor
typescript-svelte-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dummdidumm

Copy link
Copy Markdown
Member

Since we want to get rid of svelte.config.js at some point this would go the wrong way for this goal.
As discussed on discord ideally we have a way to globally enhance the component typings.

@khromov

khromov commented Jun 16, 2026

Copy link
Copy Markdown
Author

@dummdidumm Makes sense, how about this approach? #3060

@khromov khromov closed this Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants