ESLint

The Svelte preprocessor fixes scoped:class during builds, but ESLint sees a different path. eslint-plugin-svelte runs svelte/valid-compile against the source it receives from ESLint, so raw scoped:class can fail before your Svelte build preprocessor ever gets a turn.

Without the lint processor, a scoped prop can produce errors like:

Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
Unused CSS selector ".parent-owned"
Attributes should not contain ':' characters to prevent ambiguity with Svelte directives
Unused CSS selector ".parent-owned"

Use the package’s ESLint processor to run the same scoped-props transform before Svelte lint rules compile the component.

Flat config

Add the scoped props config after eslint-plugin-svelte’s flat config.

import svelte from 'eslint-plugin-svelte'
import scopedProps from '@humanspeak/svelte-scoped-props/eslint'

export default [
    ...svelte.configs['flat/recommended'],
    ...scopedProps.configs.recommended
]
import svelte from 'eslint-plugin-svelte'
import scopedProps from '@humanspeak/svelte-scoped-props/eslint'

export default [
    ...svelte.configs['flat/recommended'],
    ...scopedProps.configs.recommended
]

ESLint only supports one active processor per file. Placing this config after the Svelte config lets the Svelte parser and rules remain active while the scoped props processor supplies transformed markup for .svelte files.

Custom options

If your Svelte preprocessor uses custom hash options, pass the same options to the ESLint processor.

import svelte from 'eslint-plugin-svelte'
import { createProcessor } from '@humanspeak/svelte-scoped-props/eslint'

export default [
    ...svelte.configs['flat/recommended'],
    {
        files: ['**/*.svelte'],
        processor: createProcessor({
            cssHash: ({ css, filename, hash }) => `my-${hash(filename ?? css)}`
        })
    }
]
import svelte from 'eslint-plugin-svelte'
import { createProcessor } from '@humanspeak/svelte-scoped-props/eslint'

export default [
    ...svelte.configs['flat/recommended'],
    {
        files: ['**/*.svelte'],
        processor: createProcessor({
            cssHash: ({ css, filename, hash }) => `my-${hash(filename ?? css)}`
        })
    }
]

The processor accepts the same option shape as scopedProps(): cssHash, marker, normalizeFilename, and runtimeModule.

What gets transformed

Static scoped props are rewritten into normal props before linting.

<ChildCard scoped:class="parent-owned" />
<ChildCard scoped:class="parent-owned" />
<ChildCard class="parent-owned svelte-abc123" />
<ChildCard class="parent-owned svelte-abc123" />

Dynamic ClassValue expressions use the same runtime helper call as the build preprocessor.

<ChildCard scoped:class={['parent-owned', { active }]} />
<ChildCard scoped:class={['parent-owned', { active }]} />

Prop aliases work the same way.

<FancyCard scoped:internalClass="inner-panel" />
<FancyCard scoped:internalClass="inner-panel" />

Parent-side object spreads still cannot express scoped intent. Scope before the prop is collected into an object, then forward the transformed prop normally.

Message locations

The processor remaps lint messages back to the source you authored. Diagnostics from other ESLint rules should point at scoped:class or the original expression, not at the generated class={__svelte_scoped_props_class(...)} output.

Autofixes are kept when their ranges map cleanly to authored source. Fixes that would edit generated helper code or marker snippets are dropped.

CSS selector checks

For svelte/valid-compile, the processor also adds the same marker snippet used by the build preprocessor. That marker lets Svelte’s CSS analysis see the local class name, so the selector is not reported as unused just because the class is handed to a child component prop.

The marker does not render.