CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-eslint-plugin-unicorn

More than 100 powerful ESLint rules for enforcing code quality, consistency, and modern JavaScript best practices

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

dom-browser-rules.mddocs/

DOM and Browser Rules

Rules specific to DOM manipulation and browser environment patterns for web development.

Capabilities

DOM Node Manipulation Rules

Rules for modern DOM node manipulation methods.

/**
 * Prefers Node.append() over Node.appendChild()
 */
'unicorn/prefer-dom-node-append': 'error' | 'warn' | 'off';

/**
 * Prefers Node.remove() over parentNode.removeChild()
 */
'unicorn/prefer-dom-node-remove': 'error' | 'warn' | 'off';

/**
 * Prefers Node.textContent over Node.innerText
 */
'unicorn/prefer-dom-node-text-content': 'error' | 'warn' | 'off';

/**
 * Prefers dataset API over getAttribute/setAttribute for data attributes
 */
'unicorn/prefer-dom-node-dataset': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy DOM manipulation
const parent = document.getElementById('container');
const child = document.createElement('div');
parent.appendChild(child);

// ✅ Good - modern DOM manipulation
const parent = document.getElementById('container');
const child = document.createElement('div');
parent.append(child);

// ❌ Bad - legacy node removal
const element = document.getElementById('target');
element.parentNode.removeChild(element);

// ✅ Good - modern node removal
const element = document.getElementById('target');
element.remove();

// ❌ Bad - using innerText
element.innerText = 'Hello World';

// ✅ Good - using textContent
element.textContent = 'Hello World';

// ❌ Bad - manual data attribute handling
element.setAttribute('data-user-id', '123');
const userId = element.getAttribute('data-user-id');

// ✅ Good - using dataset API
element.dataset.userId = '123';
const userId = element.dataset.userId;

Element Selection Rules

Rules for modern element selection methods.

/**
 * Prefers querySelector/querySelectorAll over legacy methods
 */
'unicorn/prefer-query-selector': 'error' | 'warn' | 'off' | [
  'error' | 'warn',
  {
    checkId?: boolean; // Default: true
    checkClass?: boolean; // Default: true
    checkTag?: boolean; // Default: false
    checkAttribute?: boolean; // Default: true
  }
];

/**
 * Prefers modern DOM APIs over legacy methods
 */
'unicorn/prefer-modern-dom-apis': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy element selection
const elementById = document.getElementById('myId');
const elementsByClass = document.getElementsByClassName('myClass');
const elementsByTag = document.getElementsByTagName('div');

// ✅ Good - modern element selection
const elementById = document.querySelector('#myId');
const elementsByClass = document.querySelectorAll('.myClass');
const elementsByTag = document.querySelectorAll('div');

// ❌ Bad - legacy DOM methods
element.setAttribute('hidden', '');
const isHidden = element.hasAttribute('hidden');

// ✅ Good - modern DOM properties
element.hidden = true;
const isHidden = element.hidden;

Event Handling Rules

Rules for modern event handling patterns.

/**
 * Prefers addEventListener over on* properties
 */
'unicorn/prefer-add-event-listener': 'error' | 'warn' | 'off' | [
  'error' | 'warn',
  {
    excludedPackages?: string[]; // Default: ['koa', 'sax']
  }
];

/**
 * Prefers EventTarget over other event handling patterns
 */
'unicorn/prefer-event-target': 'error' | 'warn' | 'off';

/**
 * Prefers KeyboardEvent.key over KeyboardEvent.keyCode
 */
'unicorn/prefer-keyboard-event-key': 'error' | 'warn' | 'off';

/**
 * Prevents invalid removeEventListener calls
 */
'unicorn/no-invalid-remove-event-listener': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - on* event properties
button.onclick = handleClick;
window.onload = initialize;

// ✅ Good - addEventListener
button.addEventListener('click', handleClick);
window.addEventListener('load', initialize);

// ❌ Bad - legacy keyboard event handling
document.addEventListener('keydown', (event) => {
  if (event.keyCode === 13) { // Enter key
    handleEnter();
  }
  if (event.keyCode === 27) { // Escape key
    handleEscape();
  }
});

// ✅ Good - modern keyboard event handling
document.addEventListener('keydown', (event) => {
  if (event.key === 'Enter') {
    handleEnter();
  }
  if (event.key === 'Escape') {
    handleEscape();
  }
});

// ❌ Bad - invalid removeEventListener
element.addEventListener('click', () => handleClick());
element.removeEventListener('click', () => handleClick()); // Won't work

// ✅ Good - proper removeEventListener
const clickHandler = () => handleClick();
element.addEventListener('click', clickHandler);
element.removeEventListener('click', clickHandler);

Browser API Rules

Rules for modern browser APIs and patterns.

/**
 * Prevents document.cookie usage in favor of proper cookie handling
 */
'unicorn/no-document-cookie': 'error' | 'warn' | 'off';

/**
 * Prefers Blob reading methods over alternatives
 */
'unicorn/prefer-blob-reading-methods': 'error' | 'warn' | 'off';

/**
 * Prevents invalid fetch options
 */
'unicorn/no-invalid-fetch-options': 'error' | 'warn' | 'off';

/**
 * Requires postMessage target origin specification
 */
'unicorn/require-post-message-target-origin': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - direct document.cookie manipulation
const getCookie = (name) => {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
};

// ✅ Good - proper cookie handling with library or modern API
// Use a cookie library or the newer Cookie Store API when available
if ('cookieStore' in window) {
  const cookie = await cookieStore.get(name);
}

// ❌ Bad - manual blob reading
const reader = new FileReader();
reader.onload = (event) => {
  const text = event.target.result;
};
reader.readAsText(blob);

// ✅ Good - modern blob reading
const text = await blob.text();
const arrayBuffer = await blob.arrayBuffer();
const dataUrl = await blob.dataURL();

// ❌ Bad - invalid fetch options
fetch('/api/data', {
  method: 'GET',
  body: JSON.stringify(data), // Invalid: GET requests can't have body
});

// ✅ Good - valid fetch options
fetch('/api/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(data),
});

// ❌ Bad - postMessage without target origin
window.postMessage(data, '*');

// ✅ Good - postMessage with specific target origin
window.postMessage(data, 'https://trusted-domain.com');

Form and Input Handling Rules

Rules for modern form and input handling patterns.

/**
 * Modern DOM APIs include better form handling patterns
 */
'unicorn/prefer-modern-dom-apis': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - legacy form data handling
const form = document.forms.myForm;
const data = {};
for (const element of form.elements) {
  if (element.name) {
    data[element.name] = element.value;
  }
}

// ✅ Good - modern FormData API
const form = document.forms.myForm;
const formData = new FormData(form);
const data = Object.fromEntries(formData);

// ❌ Bad - manual input validation
const email = input.value;
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);

// ✅ Good - built-in constraint validation
const isValid = input.checkValidity();
const validationMessage = input.validationMessage;

Custom Elements and Web Components

Rules for custom elements and web components.

/**
 * EventTarget patterns for custom events
 */
'unicorn/prefer-event-target': 'error' | 'warn' | 'off';

Usage Examples:

// ❌ Bad - manual event handling for custom elements
class MyElement extends HTMLElement {
  constructor() {
    super();
    this.listeners = new Map();
  }
  
  on(event, handler) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event).push(handler);
  }
  
  emit(event, data) {
    if (this.listeners.has(event)) {
      this.listeners.get(event).forEach(handler => handler(data));
    }
  }
}

// ✅ Good - using EventTarget for custom elements
class MyElement extends HTMLElement {
  constructor() {
    super();
  }
  
  emitCustomEvent(type, detail) {
    this.dispatchEvent(new CustomEvent(type, { detail }));
  }
}

// Usage
const element = new MyElement();
element.addEventListener('custom-event', (event) => {
  console.log(event.detail);
});
element.emitCustomEvent('custom-event', { data: 'value' });

Performance and Optimization Rules

Rules for DOM performance optimization.

/**
 * Query selector optimization patterns
 */
'unicorn/prefer-query-selector': [
  'error',
  {
    checkId: true,    // querySelector('#id') over getElementById
    checkClass: true, // querySelectorAll('.class') over getElementsByClassName
    checkTag: false,  // Allow getElementsByTagName for performance
  }
];

Usage Examples:

// Performance considerations:

// ❌ Less efficient for simple ID lookups (sometimes)
const element = document.querySelector('#myId');

// ✅ More efficient for simple ID lookups (sometimes)
const element = document.getElementById('myId');

// But querySelector is more consistent and flexible:
// ✅ Consistent API for complex selectors
const elements = document.querySelectorAll('.class[data-active="true"]');

// ✅ Better for chaining and complex queries
const specificElement = container.querySelector('.item:nth-child(3)');

Configuration Examples

Comprehensive DOM and Browser Rules

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // DOM manipulation
      'unicorn/prefer-dom-node-append': 'error',
      'unicorn/prefer-dom-node-remove': 'error',
      'unicorn/prefer-dom-node-text-content': 'error',
      'unicorn/prefer-dom-node-dataset': 'error',
      
      // Element selection
      'unicorn/prefer-query-selector': ['error', {
        checkId: true,
        checkClass: true,
        checkTag: false, // Keep for performance
        checkAttribute: true,
      }],
      'unicorn/prefer-modern-dom-apis': 'error',
      
      // Event handling
      'unicorn/prefer-add-event-listener': 'error',
      'unicorn/prefer-event-target': 'error',
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-remove-event-listener': 'error',
      
      // Browser APIs
      'unicorn/no-document-cookie': 'error',
      'unicorn/prefer-blob-reading-methods': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/require-post-message-target-origin': 'error',
    },
  },
];

React/SPA-Friendly Configuration

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // DOM manipulation (less relevant in React)
      'unicorn/prefer-dom-node-append': 'warn', // React handles this
      'unicorn/prefer-dom-node-remove': 'warn', // React handles this
      'unicorn/prefer-dom-node-text-content': 'error',
      'unicorn/prefer-dom-node-dataset': 'error',
      
      // Event handling (important for React)
      'unicorn/prefer-add-event-listener': ['error', {
        excludedPackages: ['react', '@types/react']
      }],
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-remove-event-listener': 'error',
      
      // Browser APIs (still relevant)
      'unicorn/no-document-cookie': 'error',
      'unicorn/prefer-blob-reading-methods': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/require-post-message-target-origin': 'error',
    },
  },
];

Legacy Browser Support Configuration

export default [
  {
    plugins: {
      unicorn: eslintPluginUnicorn,
    },
    rules: {
      // Be more lenient with modern APIs for legacy support
      'unicorn/prefer-modern-dom-apis': 'warn',
      'unicorn/prefer-blob-reading-methods': 'off', // May not be supported
      
      // Still enforce good practices
      'unicorn/prefer-add-event-listener': 'error',
      'unicorn/prefer-keyboard-event-key': 'error',
      'unicorn/no-invalid-fetch-options': 'error',
      'unicorn/no-document-cookie': 'warn', // May be necessary fallback
      
      // Query selector support varies
      'unicorn/prefer-query-selector': ['warn', {
        checkId: false,    // getElementById has better support
        checkClass: false, // getElementsByClassName has better support  
        checkTag: false,   // getElementsByTagName has better support
      }],
    },
  },
];

docs

anti-pattern-prevention-rules.md

array-object-rules.md

code-quality-rules.md

code-style-rules.md

dom-browser-rules.md

import-export-rules.md

index.md

modern-javascript-rules.md

plugin-configuration.md

tile.json