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

@ezstart/eslint-plugin-ezstart

v1.0.1

Published

Custom ESLint rules that codify @ezstart monorepo conventions (api-sdk, no raw fetch/html, parseApiError, AlertDialog over confirm).

Readme

@ezstart/eslint-plugin-ezstart

Custom ESLint rules that codify @ezstart monorepo conventions (HTTP client, typography, confirm dialogs, error parsing).

Install

pnpm add -D @ezstart/eslint-plugin-ezstart

Requires ESLint 8 or 9 as a peer dependency.

Quickstart (monorepo)

Add the plugin's recommended config to your flat eslint.config.js:

import ezstart from '@ezstart/eslint-plugin-ezstart'

export default [
  // ...other configs
  {
    plugins: { '@ezstart/ezstart': ezstart },
    rules: ezstart.configs.recommended.rules,
  },
]

Or cherry-pick individual rules:

import ezstart from '@ezstart/eslint-plugin-ezstart'

export default [
  {
    plugins: { '@ezstart/ezstart': ezstart },
    rules: {
      '@ezstart/ezstart/no-raw-fetch': 'error',
      '@ezstart/ezstart/no-alert-confirm': 'error',
    },
  },
]

Quickstart (external / standalone)

The plugin is published as a standard ESLint plugin (flat config). Drop it into any ESLint 8/9 project:

// eslint.config.js
import ezstart from '@ezstart/eslint-plugin-ezstart'

export default [
  {
    plugins: { '@ezstart/ezstart': ezstart },
    rules: ezstart.configs.recommended.rules,
  },
]

No runtime dependency on the rest of the @ezstart monorepo.

Presets

| Preset | Severity policy | Use when | | ------------- | ------------------------------------------------------------- | --------------------------------- | | recommended | error on hard rules, warn on rules exposing existing debt | Default — adopt progressively | | strict | All rules set to error | After sweeping the codebase clean |

import ezstart from '@ezstart/eslint-plugin-ezstart'

export default [
  {
    plugins: { '@ezstart/ezstart': ezstart },
    rules: ezstart.configs.strict.rules, // or .recommended
  },
]

API

no-fetch-client

Disallows import … from '@ezstart/fetch-client'. The deprecated package was replaced by @ezstart/api-sdk.

  • Severity: error
  • Autofix: yes (rewrites the module specifier)
// ❌
import { apiCall } from '@ezstart/fetch-client'

// ✅
import { apiCall } from '@ezstart/api-sdk'

no-raw-fetch

Disallows calling the global fetch() (and window.fetch, globalThis.fetch) inside apps/<app>/web/src/**/*.{ts,tsx}.

  • Severity: error
  • Autofix: no (rewrite needs human context)
  • Ignored: Next.js route handlers under apps/<app>/web/src/app/api/**
  • Sanctioned wrapper: fetchExternal() from @ezstart/api-sdk is never flagged.
// ❌
const res = await fetch('/api/users')

// ✅ internal
import { apiCall } from '@ezstart/api-sdk'
const users = await apiCall('myapp', '/api/users')

// ✅ 3rd-party
import { fetchExternal } from '@ezstart/api-sdk'
const rates = await fetchExternal('https://api.example.com/rates')

parse-api-error-required

Flags throw new Error(X.error || '…') patterns where X is a variable — X.error is often an ErrorPayload object that stringifies to [object Object].

  • Severity: error
  • Autofix: yes, only if parseApiError is already imported in the file.
// ❌
throw new Error(response.error || 'Failed')

// ✅
import { parseApiError } from '@ezstart/api-sdk'
throw new Error(parseApiError(response.data) ?? 'Failed')

no-raw-html

In apps/<app>/web/src/**/*.{ts,tsx}, flags native HTML elements that have a documented @ezstart/ui/components replacement.

  • Severity: error
  • Autofix: no (replacement needs an import + often a variant/size)
  • Flagged tags: p, span, h1–h6, button, input, textarea, select, a
  • Allowed (layout): div, main, aside, header, footer, nav, section, article, ul, ol, li, figure, figcaption
// ❌
<h1>Dashboard</h1>
<button onClick={onClick}>Save</button>

// ✅
import { H1, Button } from '@ezstart/ui/components'
<H1>Dashboard</H1>
<Button onClick={onClick}>Save</Button>

no-alert-confirm

Disallows alert(), confirm(), prompt() and their window.* / globalThis.* variants.

  • Severity: error
  • Autofix: no (replacement is structurally different — <AlertDialog> is async)
// ❌
if (window.confirm('Delete?')) deleteItem()

// ✅ destructive confirm
import { AlertDialog } from '@ezstart/ui/components'
// render <AlertDialog /> with open state + onConfirm handler

// ✅ notification
import { toast } from 'sonner'
toast.error('Delete failed')

no-console-log

Disallows console.log, console.warn, console.error, console.info, console.debug in source files. Use @ezstart/logger for consistent leveled logging.

  • Severity: warn (recommended) / error (strict)
  • Autofix: no
  • Allowed: __tests__/, *.test.ts, *.spec.ts, scripts/, bin/, *.config.ts, packages/logger/**
// ❌
console.log('user logged in')
console.error('boom', err)

// ✅
import { logger } from '@ezstart/logger'
logger.info('user logged in')
logger.error('boom', err)

no-hardcoded-tailwind-colors

Flags raw Tailwind palette utilities (bg-red-500, text-gray-700, border-indigo-200, ...) in JSX className, class-name helpers (cn, clsx, classnames, tw). Forces semantic tokens.

  • Severity: warn (recommended) / error (strict)
  • Autofix: no (the right semantic token depends on intent)
// ❌
<div className="bg-gray-100 text-gray-900 border-gray-200" />
<button className="bg-indigo-500 hover:bg-indigo-600 text-white" />

// ✅
<div className="bg-card text-foreground border" />
<button className="bg-primary hover:bg-primary/90 text-primary-foreground" />

no-dialog-outside-ui

Blocks Dialog / DialogContent / DialogHeader / ... imports from @ezstart/ui outside packages/ui/. App and SDK code must use higher-level abstractions.

  • Severity: warn (recommended) / error (strict)
  • Autofix: no
// ❌
import { Dialog, DialogContent } from '@ezstart/ui/components'

// ✅ generic modal
import { Modal } from '@ezstart/ui/components'

// ✅ destructive confirm
import { AlertDialog } from '@ezstart/ui/components'

// ✅ notification
import { toast } from 'sonner'

require-i18n-string

Heuristic-based. Warns when a hardcoded user-facing string (2+ words, starts with uppercase) appears in:

  • JSX text: <Button>Save Changes</Button>
  • User-facing props: placeholder, label, title, description, aria-label, alt, helperText
  • toast.success/error/info/... arguments
  • alert(), confirm(), prompt() arguments

Scoped to apps/*/web/src/** and packages/{ui,*-sdk}/src/components/**.

  • Severity: warn (recommended) / error (strict)
  • Autofix: no
// ❌
<Button>Save Changes</Button>
<Input placeholder="Enter your email" />
toast.success('Profile updated successfully')

// ✅
const t = useTranslations('profile')
<Button>{t('saveChanges')}</Button>
<Input placeholder={t('emailPlaceholder')} />
toast.success(t('profileUpdated'))

no-local-ui-components

Warns when apps/<app>/web/src/components/** defines a visual primitive (intrinsic JSX element with 3+ Tailwind-like className tokens). Visual primitives should live in @ezstart/ui or an SDK components/ layer.

  • Severity: warn
  • Autofix: no
// ❌ apps/myapp/web/src/components/FancyCard.tsx
export const FancyCard = ({ children }) => (
  <div className="flex items-center gap-4 rounded-md bg-card p-4">{children}</div>
)

// ✅ app does composition only
import { Card, CardContent } from '@ezstart/ui/components'
export const MyPage = () => (
  <Card>
    <CardContent>...</CardContent>
  </Card>
)

Migration

This is a new package — there is nothing to migrate from. Existing projects adopt it incrementally by switching one rule to error at a time.

The rules no-express-core and no-fetch-client are set to error — both legacy packages have been fully migrated and deleted (replaced by @ezstart/api-core and @ezstart/api-sdk respectively).

Related

  • standard.md — rule 7 requires every sensitive package to own at least one rule here
  • @ezstart/api-sdk — HTTP client that replaces the deprecated @ezstart/fetch-client
  • @ezstart/ui — the component library that no-raw-html steers code towards
  • @ezstart/eslint-config — shared flat configs that will eventually enable this plugin