npm.io
21.0.0 • Published 5d ago

@plumeria/eslint-plugin

Licence
MIT
Version
21.0.0
Deps
0
Vulns
0
Weekly
0
Stars
23

@plumeria/eslint-plugin

ESLint plugin for Plumeria.
Below are the available rules and the recommended configuration.

The plugin:@plumeria/recommended config enables the following:

  • @plumeria/custom-props-require-import: error
  • @plumeria/expand-border-shorthands: warn
  • @plumeria/no-combinator: error
  • @plumeria/no-destructure: error
  • @plumeria/no-inline-object: error
  • @plumeria/no-inner-call: error
  • @plumeria/no-invalid-selector: error
  • @plumeria/no-mixed-styling-props: error
  • @plumeria/no-mutable-binding: warn
  • @plumeria/no-order-dependent-overlap: warn
  • @plumeria/no-style-prop-relay: error
  • @plumeria/no-unknown-css-properties: error
  • @plumeria/no-unresolved-composition: warn
  • @plumeria/no-unused-keys: warn
  • @plumeria/sort-properties: warn
  • @plumeria/format-properties: warn
  • @plumeria/validate-values: warn
  • @plumeria/validate-pseudos: warn
  • @plumeria/validate-at-rules: warn
import plumeria from '@plumeria/eslint-plugin';

export default [plumeria.configs.recommended];

Configuring the styling prop

custom-props-require-import, no-inline-object, no-mixed-styling-props, no-style-prop-relay and no-unresolved-composition need to know which JSX prop carries styles. It is classStyle unless you changed it, so most projects configure nothing.

If you did rename it — via styleProp on withPlumeria or on the unplugin options — tell the plugin the same name. The lint the bundler plugins run in the build is given that name already. ESLint cannot read it from your bundler config, so set it once in settings and every rule picks it up:

import plumeria from '@plumeria/eslint-plugin';

export default [
  plumeria.configs.recommended,
  {
    settings: {
      plumeria: { styleProp: 'sx' },
    },
  },
];

A single rule can override that if you need it to:

{
  rules: {
    '@plumeria/no-mixed-styling-props': ['error', { styleProp: 'sx' }],
  },
}

The name must match whatever the loader or unplugin was given. If they disagree, the lint rules report against a prop the compiler never transforms.

The following rules are enabled by plumeria.configs.recommended with the severity shown in Recommended configuration.

custom-props-require-import

Require the @plumeria/core import in a file that passes a css.create style to a component through a prop other than the styling prop. The compiler cannot tell that such a prop carries a style, so it compiles the file only when the file imports @plumeria/core. A file that writes the styling prop itself is compiled without the import.

Accepts { styleProp }; see Configuring the styling prop.

expand-border-shorthands

Expands a border shorthand that bundles a width, a style and a color — border, borderBlock, borderInline, and the eight edge forms — into the three declarations it stands for. Those bundles are the only properties left that cross an axis shorthand without either containing the other, so expanding them turns the last unrankable pairs into ordinary shorthand-to-longhand ones.

Fixable. A value it cannot split, such as var(--edge) or inherit, is reported without a fix: leaving it silent would let the expanded declarations elsewhere outrank it.

borderTop: '1px solid red'
// becomes
borderTopWidth: '1px', borderTopStyle: 'solid', borderTopColor: 'red'

A shorthand resets what it omits, so borderBlock: 'solid' expands with medium and currentcolor written out.

no-combinator

Disallow combinators >, +, ~ and descendant combinator (space) unless inside functional pseudo-classes.

no-destructure

Disallow destructuring APIs.

no-inline-object

Disallow passing inline object to classStyle and css.use(). Only compiled styles from css.create() are allowed.

no-inner-call

Disallow calling APIs inside functions.

no-invalid-selector

Disallow invalid selector inside css.create() and css.keyframes() and css.viewTransitionClass().
create() example: (Pseudo -> Query, Query -> Query)
Also reports a functional pseudo-class nested inside one of the same name, such as :where(:where(.a), .b), and flattens it with a fix when the nested call is a whole argument. :not() may nest inside :not(). Reports a key nested deeper than 16 levels or holding a stray quote.

no-mixed-styling-props

Disallow mixing the styling prop with className or style. classStyle can handle both className and style.

Accepts { styleProp }; see Configuring the styling prop.

no-order-dependent-overlap

Warns when two properties in one style overlap but neither outranks the other, so the one written last wins. It covers one property under its logical and its physical name, and two shorthands that cross without either containing the other. A shorthand and its longhand are ranked by specificity and never reported.

It also reports two conditions in one style where one provably matches a subset of the other — (min-width: 900px) inside (min-width: 600px), or two queries naming the same container — and the narrower one is written first. The optimizer places the narrower one last whatever the source says, so the declarations read the wrong way round. The swap is offered as a suggestion rather than a fix.

A pair of spellings is one property in a horizontal writing mode and two properties in a vertical one, which is per element and cannot be read from the source. The rule reads them as one property; disable the line where an element is known to be vertical.

no-style-prop-relay

Reports a style prop that a component receives but only passes on. Apply it to the styling prop or css.use() on an element the component renders.

Accepts { styleProp }; see Configuring the styling prop.

no-unknown-css-properties

Disallow unknown CSS properties in camelCase within css.create, css.keyframes, css.viewTransitionClass, and css.positionTry. Inside css.positionTry, it also reports properties that @position-try does not accept.

In css.create, a key that holds an object is reported with a hint that nested selectors start with :, [ or @.

no-unresolved-composition

A safety net for css.use(). Warns when its result is combined with other class names or passed to another function, or when a function key is passed to it. Pass every style to one css.use() call, and apply function keys to the styling prop.

Accepts { styleProp }; see Configuring the styling prop.

no-unused-keys

Warns when object keys are defined but not used, mainly in component files.

sort-properties

Automatically sorts CSS properties in the recommended order for consistency and maintainability.

format-properties

Automatically format for consistency and maintainability.

  • Formats a line into a multi-line.
  • Formats by filling in blank lines.
validate-values

Validates CSS property values for correctness. Only standard CSS properties are checked; properties with string literal keys (e.g., computed or dynamic property names) are not validated.

validate-pseudos

Validates CSS pseudo-classes and pseudo-elements inside css.create(). It checks for typos and structural correctness and supports validation of computed keys when TypeScript is available.

validate-at-rules

Validates at-rules inside css.create(). It accepts @media, @container, and @supports, each with a prelude, whether or not a space separates it from the keyword, and @starting-style on its own, and supports validation of computed keys when TypeScript is available.

Optional rules

These rules are not enabled by plumeria.configs.recommended. Enable the spelling policy that fits the project:

export default [
  plumeria.configs.recommended,
  {
    rules: {
      '@plumeria/no-logical-properties': 'warn',
    },
  },
];
no-physical-properties / no-logical-properties

Disallow one of the two names a property can carry, so a project writes edges under one spelling only. no-physical-properties reports paddingLeft and suggests paddingInlineStart; no-logical-properties reports the reverse. Accepts { sizes }, which adds width, height, their min/max forms, overflow-x and overflow-y to the properties covered. It is false by default: a size is one property under either spelling, while an edge is what a direction reverses.

Turning both rules on is contradictory. Reach for one when you want the pairs no-order-dependent-overlap reports to be impossible to write: a property that appears under one spelling only can never meet its other spelling on an element. A shorthand with no single counterpart, such as borderBlockWidth, is outside either rule and stays reported.

The bundler plugins turn the matching rule on in the build lint as well: withoutPhysicalProperties runs no-physical-properties, and withoutLogicalProperties runs no-logical-properties, { sizes } included.

CLI (plumeria-lint)

This package provides a CLI command, plumeria-lint, as a convenient way to run Plumeria's custom ESLint rules.

It runs oxlint, which this package depends on, for fast linting with code snippets in output, so oxlint needs no separate install.

Installation
npm install -D @plumeria/eslint-plugin
# or
pnpm add -D @plumeria/eslint-plugin
Usage
plumeria-lint

The process exits with a non-zero status code if any errors or warnings are found, making it suitable for use in CI and build pipelines.

Styling prop

If the styling prop is renamed, pass the name with --style-prop. Without the flag, plumeria-lint asks ESLint for settings.plumeria.styleProp when ESLint and a flat config are installed, and uses classStyle otherwise:

plumeria-lint --style-prop sx
Aborting Builds on Lint Errors (Parallel Pipeline)

You can run plumeria-lint in parallel with your build command (e.g. next build or vite build) using the -- separator:

{
  "scripts": {
    "build": "plumeria-lint -- next build"
  }
}

If plumeria-lint detects any styling errors or warnings, it will print the diagnostics, kill the build process immediately, and exit with a non-zero code. This avoids compiling when styling validation fails.

With @plumeria/next-plugin and @plumeria/unplugin (except on esbuild and Bun), this lint is integrated into the build by default, so it needs no setup. Pass lint: false to the plugin to build without it.

Keywords