Skip to content

Staticview ESLint Plugin

@staticview/eslint-plugin is an ESLint plugin for authoring web components with the @staticview/core build system. It enforces patterns and constraints specific to staticview components, covering custom element conventions, build-time compatibility, DOM API usage, and framework type generation.

The plugin integrates with any staticview-based setup, including the vite-plugin, staticbolt-plugin, and cli.

Requirements

  • ESLint with flat config support (ESLint 9+)

Installation

Terminal window
npm install --save-dev @staticview/eslint-plugin

Configuration

Import the plugin and extend the recommended config in your eslint.config.js. The recommended config enables all plugin rules.

import sv from "@staticview/eslint-plugin";
export default [
{
files: ["path/to/components/**/*.ts"],
extends: [sv.configs.recommended],
},
];

Overriding rules

Any rule can be overridden after extending recommended:

import sv from "@staticview/eslint-plugin";
export default [
{
files: ["src/components/**/*.ts"],
extends: [sv.configs.recommended],
rules: {
"sv/compat-dom": ["error", { available: "newly", newlyMinYears: 0.9 }],
},
},
];

Targeting component files only

The plugin rules are designed specifically for staticview component files and should not be applied globally. Always scope the configuration to your component source files using the files glob. Applying these rules to non-component files will produce false positives.

Rules

check-macros

Validates usage of the $inline and $shadow build-time macros.

Checks include:

  • Verifies that files referenced by $inline and $shadow exist.
  • Ensures htmlFile and htmlString are not used together in $shadow options.
  • Ensures cssFile and cssString are not used together in $shadow options.

$inline is a build-time macro that replaces the call with the contents of the HTML or CSS file specified as its first argument. Referenced files are treated as part of the component and are processed and minified together with it. An error is reported if the file cannot be resolved.

$shadow is a build-time macro that generates the boilerplate required to attach a shadow root, inline HTML and CSS, and clone the template for each component instance. The HTML template and CSSStyleSheet are created once as static private fields and shared across all instances of the component class.


custom-element-name

Enforces a valid custom element name via the _componentName static property.

Every component must declare a _componentName static property. This rule validates that:

  • _componentName is defined on the class
  • _componentName is a string literal
  • _componentName is not a reserved name
  • _componentName does not start with xml
  • _componentName contains at least one hyphen
  • _componentName is entirely lowercase
  • _componentName begins with a lowercase letter, not a number or special character

compat-dom

Warns when using DOM APIs that are unsupported by your browserslist targets or below a specified baseline availability threshold.

Options

available

  • Type: `“widely” \
  • Default: “newly” \
  • Description: “limited”`

browserslist

  • Type: string[]
  • Description: Browserslist targets to check against.

partial

  • Type: boolean
  • Description: Also warn on APIs with partial browser support.

newlyMinYears

  • Type: number
  • Description: Only applies when available is "newly". Warn if a newly-available (baseline: low) feature has been available across all browsers for fewer than this many years, computed from baseline_low_date. For example, 1 warns if the feature landed in all browsers less than one year ago.

export-component-types

Enforces that each component file exports a corresponding [ComponentClassName]Types type.

This type is used to generate framework-specific types and must be exported from the component file. It should be defined using SV.WComponent<typeof ClassName>.

Example:

export type AccordionTypes = SV.WComponent<typeof Accordion>;
class Accordion extends HTMLElement implements SV.IWebComponent {
// ...
}

no-dynamic-css-class-name

Disallows dynamically constructed CSS class names.

At build time, CSS class names are minified and renamed. Dynamically constructed class names (e.g. via string concatenation or template literals) cannot be statically analyzed, and will not be updated during minification, causing them to break at runtime.

If this rule is disabled or suppressed with an inline comment, dynamic class names will not trigger a warning during minification and will remain unmodified. Only disable this rule when you are certain the class names are intentionally static strings that should not be renamed.

Options

ignorePrefix

  • Type: string[]
  • Default: ["_"]
  • Description: List of class name prefixes to ignore.

no-dynamic-css-variable

Disallows dynamically constructed CSS variable names.

At build time, CSS variable names are minified and renamed. Dynamically constructed variable names cannot be statically analyzed and will not be updated during minification, causing them to break at runtime.

If this rule is disabled or suppressed with an inline comment, dynamic variable names will not trigger a warning during minification and will remain unmodified. Only disable this rule when you are certain the variable names are intentionally static strings that should not be renamed.

Options

ignorePrefix

  • Type: string[]
  • Default: ["sv-", "_"]
  • Description: List of variable name prefixes to ignore.

no-import-export-value

Disallows importing or exporting runtime values from web component files.

At build time, component files are wrapped in an IIFE to ensure compatibility with non-module <script> tags. Importing or exporting runtime values is incompatible with this transformation. Type-only imports and exports (import type, export type) are permitted.


no-invalid-event-name

Disallows event names that are reserved, conflict with DOM events, or are incompatible with other frameworks.

Event names must consist of lowercase letters only. Hyphens, numbers, and special characters are not allowed. Event names must not start with on, as this conflicts with framework event-binding conventions (e.g. onClick, onSubmit).

Options

ignored

  • Type: string[]
  • Description: Event names to suppress warnings for.

extend

  • Type: string[]
  • Description: Additional event names to explicitly allow.

no-this-element-selector

Disallows querying child elements directly on the web component’s host element.

Querying child elements on this (e.g. via this.querySelector) is unreliable because child elements may not have been rendered yet at the time of the call. Use slots and the slotchange event to respond to child content.


no-true-default-attribute

Disallows boolean observed attributes from defaulting to true.

Boolean HTML attributes cannot be set to false via markup — their mere presence sets them to true, and they can only be removed entirely. Defaulting a boolean attribute to true therefore creates an irreversible state: there is no way for a user to opt out using HTML alone. Boolean observed attributes must default to false or undefined.


observed-attributes

Enforces that observedAttributes is declared as a public static getter returning a tuple of string literals annotated with as const.

Using string[] as the return type loses the literal types of each attribute name, which breaks type-level integrations that depend on knowing the exact attribute strings. The as const annotation preserves the tuple’s literal types and ensures the type system can narrow correctly.

A member is considered public when it is not prefixed with #.


prefer-owner-document

Requires web component instance code to use this.ownerDocument instead of the global document.

An element can be adopted into another document, such as an iframe. Using its owner document ensures that DOM queries, element creation, events, and document state refer to the document that currently owns the component. Static class members are reported without an autofix because they must receive a Document or derive ownerDocument from an element argument.


prefer-static-private-methods

Flags private methods and arrow function properties that do not reference this and could be declared static.

Methods that do not use this have no reason to be instance members. Marking them static makes the intent explicit, avoids unnecessary access to the instance, and can allow the engine to optimize the call.

Private members are identified by the # prefix.


arrow-public-methods

Enforces that public methods on HTMLElement subclasses are declared as readonly arrow function properties.

Regular prototype methods and arrow function class properties are structurally equivalent at runtime, but differ in how the type system treats them. Declaring public methods as readonly arrow functions allows the type system to distinguish between assignable function properties and non-assignable prototype methods, which is required for accurate framework type generation.


consistent-class-member-order

Enforces a consistent ordering of members within staticview component classes.

Keeping members in a predictable order improves readability, makes components easier to navigate, and ensures a consistent structure across projects. The rule also preserves getter/setter pairs and lifecycle callback ordering.

By default, membersOrder are grouped in the following order:

  1. "private-static-fields"
  2. "private-instance-fields"
  3. "public-static-fields"
  4. "public-instance-fields"
  5. "constructor"
  6. "lifecycle-hooks"
  7. "public-static-methods"
  8. "public-instance-methods"
  9. "private-static-methods"
  10. "private-instance-methods"

Getter/setter pairs are kept together. By default accessorsOrder are grouped if shouldGroupAccessors is true in the following order:

  1. "getter"
  2. "setter"
  3. "field"

By default, lifecycleHooksOrder are grouped in the following order:

  1. "connectedCallback"
  2. "connectedMoveCallback"
  3. "disconnectedCallback"
  4. "attributeChangedCallback"
  5. "adoptedCallback"
  6. "checkValidity"
  7. "reportValidity"
  8. "formAssociatedCallback"
  9. "formResetCallback"
  10. "formStateRestoreCallback"
  11. "formDisabledCallback"

Options

membersOrder

  • Type: string[]
  • Description: Overrides the default order of member groups.

accessorsOrder

  • Type: string[]
  • Description: Overrides the default order of accessor groups.

lifecycleHooksOrder

  • Type: string[]
  • Description: Overrides the default order of lifecycle hook groups.

shouldGroupAccessors

  • Type: boolean
  • Description: Whether accessors should be grouped together. Use accessorsOrder to control the order of accessors.

shouldReadonlyBeFirst

  • Type: `boolean \
  • Description: null`

shouldUnderscorePrivateBeFirst

  • Type: `boolean \
  • Description: null`

excludedMemberNames

  • Type: string[]
  • Description: List of member names to exclude from ordering.

This rule is automatically fixable with eslint --fix.


explicit-class-member-types

Enforces explicit type annotations on class property declarations.

Declaring the type explicitly gives the analyzer a reliable source of truth to read directly, bypassing inference entirely during build time.

Options

shouldCheckStaticMembers

  • Type: boolean
  • Default: false
  • Description: Check static property members.

shouldCheckInstanceMembers

  • Type: boolean
  • Default: true
  • Description: Check instance (non-static) property members.

shouldCheckPrivateMembers

  • Type: boolean
  • Default: false
  • Description: Check private members (prefixed with # or _), whether static or instance.

shouldCheckArrowFunctionMembers

  • Type: boolean
  • Default: false
  • Description: Check members whose initializer is an arrow function or function expression.

This rule is automatically fixable with eslint --fix.


require-public-property-jsdoc

Enforce JSDoc comments on public class instance properties to ensure they are picked up by the documentation generator.

A property is considered public when it is not prefixed with # or _. For properties that have a getter, setter, and/or field, only one of them needs a JSDoc comment to satisfy the rule. If none has one, the getter is reported first, then the setter, then the field.

This rule is automatically fixable with eslint --fix. The fixer inserts a JSDoc stub including a TODO reminder.