eslint-plugin-sinjs
v0.0.1
Published
ESLint rules for sin.js - Tagged template call structure and general indentation in one rule
Maintainers
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-devRequires 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
indentrule from your config.sin/indentruns the builtin indent rule internally with the correct protections, so configuring both will cause conflicting fixes. AnyignoredNodesworkarounds 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 indentAfter
// eslint-disable-next-line sin/indent