npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-sinjs

v0.0.1

Published

ESLint rules for sin.js - Tagged template call structure and general indentation in one rule

Readme

eslint-plugin-sinjs

ESLint rules for sin codebases.

The main rule, sin/indent, is a complete indentation rule. It understands sin's tagged template call structure, view composition, component factories and enforces its geometry, while delegating everything else (plain functions, objects, statements) to ESLint's builtin indent rule, which it runs internally. You use this rule instead of indent, not alongside it.

Installation

This plugin is exposed via the NPM Registry, however please note that sin is still not available for public usage and exists through invitation only.

<sin|npm|pnpm|yarn> install eslint-plugin-sinjs --save-dev

Requires ESLint 9+ (flat configuration).

Usage

// eslint.config.js
import { defineConfig } from 'eslint/config'
import sin from 'eslint-plugin-sinjs'

export default defineConfig(
  {
    plugins: {
      sin
    },
    rules: {
      'sin/indent': ['error', { indent: 2 }]
    }
  }
)

Or use the recommended config, which ships sin/indent together with a full set of core ESLint style rules (comma-first, aligned keys, no semicolons, single quotes, sort-imports member sorting, plus browser/node globals and **/*.js file matching:

import { defineConfig } from 'eslint/config'
import sin from 'eslint-plugin-sinjs'

export default defineConfig(
  sin.recommended
)

Anything you add after it overrides it, so local adjustments are just another config object:

export default defineConfig(
  sin.recommended,
  {
    rules: {
      'no-console': 'off'
    }
  }
)

[!NOTE] Remove any indent rule from your config. sin/indent runs the builtin indent rule internally with the correct protections, so configuring both will cause conflicting fixes. Any ignoredNodes workarounds you previously carried for tagged template calls can be deleted as well as that is handled inside the plugin.

Options

{
  'sin/indent': ['error', {
    indent    : 2,             // indent size in spaces (default 2)
    factories : ['s'],         // identifiers treated as sin view factories (default ['s'])
    core      : {              // passed through to the builtin indent rule
      VariableDeclarator: {
        var: 1,
        let: 1,
        const: 2
      }
    }
  }]
}

indent

The indentation unit. Tabs in leading whitespace are converted, one tab per unit.

factories

Names of the view factory function. A call like s(...) marks its region as sin territory even when no tagged template appears in it. If you import the factory under another name, add it here.

core

An options object forwarded to the builtin indent rule for the code outside sin territory. Anything the builtin rule accepts (SwitchCase, VariableDeclarator, extra ignoredNodes, etc etc) works here.

How it works

Every line in a file has exactly one owner, decided by a territory model:

Tagged spans

A called tagged template (s`css`(...)) and everything inside it. The plugin enforces sin geometry here: object arguments hug the opening paren, subsequent arguments sit on their own line one unit in, closers return to the anchor column, template contents are re-anchored while preserving their internal shape.

Wrapper regions

Plain calls that contain sin content (foo.method(s(() => ...)), a component factory call, a component receiving views). The plugin walks these and enforces the mechanical layer with statement lines in blocks, object properties, array elements, argument placement of leaf component calls while the structural wrapping itself keeps its authored shape.

Plain code

Everything else belongs to the builtin indent rule at full power, so ordinary functions, objects and statements are formatted exactly as you'd expect from ESLint.

A few deliberate freedoms: ternary continuation lines (lines starting with ? or :) are never touched, anywhere, write your conditional chains however you like. Multi-line ${...} substitutions inside untagged template literals are respected as authored. Stacked closing parens ())) are allowed when the inner call opened on the outer call's opening line, and are split apart otherwise:

// stays — Badge opened on the .then( line
fetch(url)
  .then(r => Component(
    r.count
  ))

// splits — the argument started on its own line
s(() =>
  Component(
    count
  )
)

Fixing

The rule is fully fixable via eslint --fix. On a first run over a codebase with heavily divergent indentation, a small number of files may need a second --fix pass to fully settle (fixes cascade outside-in through deep nesting); subsequent day-to-day runs settle in one pass.

Rules

| Rule | Fixable | Description | | -------------| -------------| -------------------------------------------------------------------------------------- | | sin/indent | Yes | Indentation for sin codebases, tagged template call structure plus general indentation |

Ignores

If you previously used disable comments for the builtin rule, rename them:

Before

// eslint-disable-next-line indent

After

// eslint-disable-next-line sin/indent