CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-eslint-plugin-qwik

ESLint plugin providing comprehensive linting rules specifically designed for the Qwik framework with performance-focused development patterns

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

quality-rules.mddocs/

Code Quality Rules

Rules for enforcing Qwik-specific code quality patterns and performance optimizations.

Capabilities

Prefer Classlist

Enforces using the classlist prop over importing a classnames helper.

/**
 * Enforces using classlist prop over classnames helper
 * The classlist prop accepts an object { [class: string]: boolean } like classnames
 */
preferClasslist: Rule;

Rule Configuration:

  • Type: suggestion rule
  • Category: Code Quality
  • Recommended: warn level
  • Strict: error level

Options Schema:

{
  type: 'object',
  properties: {
    classnames: {
      type: 'array',
      description: 'An array of names to treat as classnames functions',
      default: ['cn', 'clsx', 'classnames'],
      items: {
        type: 'string',
        minItems: 1,
        uniqueItems: true
      }
    }
  },
  additionalProperties: false
}

Usage Example:

// ❌ Invalid: using classnames helper
import classnames from 'classnames';

<div className={classnames({
  'active': isActive,
  'disabled': isDisabled
})} />

// ✅ Valid: using classlist prop
<div class={{
  'active': isActive,
  'disabled': isDisabled
}} />

Loader Location

Detects declaration location of loader$ functions to ensure proper placement.

/**
 * Detects declaration location of loader$ functions
 * Ensures loaders are declared in appropriate locations for proper SSR
 */
loaderLocation: Rule;

Rule Configuration:

  • Type: problem rule
  • Category: Code Quality
  • Recommended: warn level
  • Strict: error level

Options Schema:

{
  type: 'object',
  properties: {
    routesDir: {
      type: 'string',
      default: 'src/routes'
    }
  },
  additionalProperties: false
}

Usage Example:

// ❌ Invalid: loader in wrong location
export default component$(() => {
  const data = routeLoader$(() => { // Error: loader should be at module level
    return { message: 'Hello' };
  });
  
  return <div>{data.value.message}</div>;
});

// ✅ Valid: loader at module level
export const useData = routeLoader$(() => {
  return { message: 'Hello' };
});

export default component$(() => {
  const data = useData();
  return <div>{data.value.message}</div>;
});

No Use Visible Task

Detects useVisibleTask$() functions and suggests alternatives.

/**
 * Detects useVisibleTask$() functions
 * Suggests alternatives for better performance and SSR compatibility
 */
noUseVisibleTask: Rule;

Rule Configuration:

  • Type: suggestion rule
  • Category: Performance
  • Recommended: warn level
  • Strict: warn level

Usage Example:

// ⚠️ Discouraged: useVisibleTask for simple effects
export default component$(() => {
  useVisibleTask$(() => { // Warning: consider alternatives
    console.log('Component visible');
  });
  
  return <div>Content</div>;
});

// ✅ Better: use useTask$ for most cases
export default component$(() => {
  useTask$(() => {
    console.log('Component mounted');
  });
  
  return <div>Content</div>;
});

Code Quality Patterns

CSS Class Management

Classlist vs Classnames: The prefer-classlist rule encourages using Qwik's built-in classlist prop instead of external classnames libraries:

Benefits of classlist prop:

  • Native Qwik optimization
  • No additional bundle size
  • Better tree-shaking
  • Consistent with Qwik patterns

Pattern Comparison:

// External library approach (discouraged)
import cn from 'classnames';
<div className={cn('base', { active: isActive })} />

// Qwik native approach (preferred)
<div class={['base', { active: isActive }]} />
// or
<div class={{ base: true, active: isActive }} />

Loader Placement

Module-Level Declaration: Loaders should be declared at the module level for proper SSR and routing integration:

Why Module-Level:

  • Enables static analysis by Qwik's build system
  • Proper integration with routing and prefetching
  • Better performance through static optimization
  • Correct SSR timing and data availability

Pattern Examples:

// ❌ Component-level (runtime creation)
export default component$(() => {
  const loader = routeLoader$(() => getData()); // Problem: dynamic creation
  return <div>{loader.value}</div>;
});

// ✅ Module-level (static analysis)
export const usePageData = routeLoader$(() => getData());

export default component$(() => {
  const data = usePageData(); // Proper: static reference
  return <div>{data.value}</div>;
});

Visible Task Alternatives

Performance Considerations: useVisibleTask$ runs when a component becomes visible, but often other hooks are more appropriate:

Alternative Patterns:

// For initialization effects
useTask$(() => {
  // Runs on component mount (SSR + client)
});

// For resource loading
useResource$(async () => {
  // Lazy resource loading with proper SSR handling
});

// For browser-only effects
useVisibleTask$(({ track }) => {
  // Only when you specifically need visibility-based triggering
  track(() => someSignal.value);
  // Browser-only DOM manipulation
});

When to Use useVisibleTask$:

  • Intersection Observer patterns
  • Animation triggers on scroll
  • Lazy loading of non-critical resources
  • Browser-only DOM measurements

Rule Integration

Example System

Quality rules integrate with the plugin's documentation system:

// Example definitions for documentation
const preferClasslistExamples: QwikEslintExamples;
const loaderLocationExamples: QwikEslintExamples;

interface QwikEslintExamples {
  [scenario: string]: {
    good?: QwikEslintExample[];
    bad?: QwikEslintExample[];
  };
}

Configuration Integration

Rules work together to enforce consistent code quality:

// Recommended configuration balances performance and usability
{
  'qwik/prefer-classlist': 'warn',    // Suggest native patterns
  'qwik/loader-location': 'warn',     // Ensure proper placement
  'qwik/no-use-visible-task': 'warn', // Encourage alternatives
}

// Strict configuration enforces best practices
{
  'qwik/prefer-classlist': 'error',   // Require native patterns
  'qwik/loader-location': 'error',    // Enforce proper placement
  'qwik/no-use-visible-task': 'warn', // Still warn (sometimes needed)
}

Performance Impact

Bundle Size Optimization

Classlist Rule Benefits:

  • Eliminates classnames library dependency (~2-3KB)
  • Better tree-shaking with native Qwik patterns
  • Reduced runtime JavaScript execution

SSR Optimization

Loader Location Benefits:

  • Enables static analysis during build
  • Proper prefetching and data loading timing
  • Better integration with Qwik's SSR pipeline

Runtime Performance

Visible Task Alternatives:

  • useTask$ runs during SSR and hydration (better UX)
  • useResource$ provides better caching and loading states
  • useVisibleTask$ should be reserved for specific visibility-based needs

Types

// Quality rule definitions (standardized to TSESLint)
type Rule = TSESLint.RuleModule<any, any>;

// Classlist prop type
type ClassList = 
  | string 
  | string[] 
  | { [className: string]: boolean }
  | (string | { [className: string]: boolean })[];

// Loader types
type RouteLoader<T> = () => Promise<T>;
type LoaderSignal<T> = {
  readonly value: T;
};

// Task types
type TaskFn = (context: { track: <T>(signal: () => T) => T }) => void | Promise<void>;
type VisibleTaskFn = (context: { track: <T>(signal: () => T) => T }) => void | Promise<void>;

// Example system integration
interface QwikEslintExample {
  code: string;
  codeTitle?: string;
  codeHighlight?: string;
  description?: string;
}

interface QwikEslintExamples {
  [scenario: string]: {
    good?: QwikEslintExample[];
    bad?: QwikEslintExample[];
  };
}

docs

configuration.md

core-rules.md

index.md

jsx-rules.md

quality-rules.md

tile.json