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

prettier-plugin-sentiment-import-sort

v1.0.3

Published

Prettier plugin that orders imports by what each binding is (its semantic kind), not by where it lives on disk. Parses each imported file's AST and classifies every binding by its actual export shape.

Readme

prettier-plugin-sentiment-import-sort

A opinionated Prettier plugin that orders imports by what each binding is (its semantic kind), not by where it lives on disk. Path conventions are unreliable across packages; this plugin parses the imported file's AST and classifies every binding by its actual export shape.

Why

Conventional import sorters group imports by package name or path glob. That breaks down whenever the project's path conventions don't perfectly mirror the kind of code being imported — a .../Components/Foo path might export a hook, a .../Hooks/Bar path might export a constant, and so on.

This plugin classifies each binding by AST inspection of the imported source file, so the resulting order reflects what the code actually is.

Install

npm install --save-dev prettier-plugin-sentiment-import-sort

Add to your Prettier config:

{
  "plugins": ["prettier-plugin-sentiment-import-sort"]
}

Requires Prettier 3.

How imports are grouped

Imports are split into up to five blocks, separated by a blank line. Empty blocks are omitted.

  1. External — anything from outside the file's own package whose value isn't a React component. Built-in node modules, third-party packages, and value/type imports from sibling monorepo packages (except components, which go to block 5).
  2. Relative type importsimport type { ... } from relative paths.
  3. Relative functional / data — relative value imports that aren't components, hooks, or presentation-support kinds (helpers, services, error classes, state, registries).
  4. Relative kind sub-groups — a dynamic block: when 3+ relative imports share a single distinct kind (hooks, translations, theme/tokens, constants), they break out into their own visually separated block.
  5. React components — React component imports from anywhere except packages outside the monorepo.

Within each block, imports are sorted in two passes:

  1. Multi-line imports first, ascending by their longest line length (and bindings inside { ... } are sorted by length).
  2. Single-line imports next, ascending by line length.

Per-binding kind detection

For each binding, the plugin parses the imported source file and classifies the export as one of:

| Kind | AST signature | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Component | function/arrow returning JSX or React.createElement(...); OR annotated : React.FC<...>; OR wrapped in React.forwardRef/React.memo/React.lazy; OR class extends React.Component; OR styled(X)\...`| | **Hook** | function whose body recursively calls other hooks | | **Type** |export type/export interface, or import type/ inlinetypemodifier | | **Translation resource** | object literal whose top-level keys match an i18nextResourceshape | | **Theme / token object** | object literal whose keys match CSS-domain tokens (colours, spacing, etc.) | | **Constants object** |as constor primitive-only object literal | | **Service / Error class** |class X { ... }(withError` superclass → errorClass) | | Factory / utility function | function returning a non-JSX value; not a hook; not a component |

Mixed-binding statements

If an import statement bundles bindings of multiple kinds, the entire statement lives in one block. Any-component-wins: if at least one binding is a React component, the whole statement lands in block 5.

Module resolution

  • Sibling monorepo package imports resolve to the original .ts(x) source rather than the package's compiled dist/. Workspace discovery reads pnpm-workspace.yaml, package.json workspaces, or lerna.json.
  • TypeScript path aliases (compilerOptions.paths in tsconfig.json) are resolved against baseUrl, with extends chains merged. Aliased imports that resolve into the importer's own package are treated as relative; ones that land in a sibling package are treated as workspace-scoped.
  • Imports from packages outside the monorepo are treated as opaque — never inspected — and always belong in block 1.
  • Relative imports resolve against the importing file.

Options

| Option | Default | Description | | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | importSortBreakoutThreshold | 3 | Minimum count for a kind (hooks, translations, theme, constants) to break out of block 3 into its own block. | | importSortOverrides | [] | List of "pattern:block" strings. When any specifier in an import statement matches a pattern, the whole statement is forced into the matched block, bypassing AST-derived classification. See Overrides. |

Overrides

importSortOverrides lets you pin specific bindings to a chosen block when the AST-derived classification doesn't reflect what you want. The classic case: an internal module re-exports styled from styled-components, which the AST sees as a component and hoists to block 5; with an override you can keep it alongside other external imports in block 1.

{
  "plugins": ["prettier-plugin-sentiment-import-sort"],
  "importSortOverrides": ["styled:1", "^use[A-Z]:5"]
}

Each entry is a single string in the form "pattern:block":

  • Pattern is everything before the last : in the entry. It's a regex auto-anchored to the full binding name, so "styled:1" matches exactly styled, never unstyled. Use standard regex syntax for fuzzier matches: "styled|css:1", "^use[A-Z]:5", ".*Theme$:1". Patterns may themselves contain : — only the final : is treated as the separator.
  • Block is the integer after the last :. Allowed values are 1 (External), 3 (Relative functional), and 5 (Components). Block 2 (relative type imports) and block 4 (dynamic kind breakout) are auto-derived and reject manual placement; values outside 1 / 3 / 5 are silently ignored.
  • Patterns match against both the imported name and the local name of each specifier, so import { styled as s } and import { x as styled } are both caught by "styled:1".
  • If multiple bindings inside one statement match different overrides, the lowest block number wins (most-external takes precedence).
  • Malformed entries (no :, non-integer block, non-string element) are skipped without breaking the run.

Caveats

  • Only inspects .ts, .tsx, and .d.ts files. JS/Flow imports fall through to a best-effort classification.
  • Hook detection only follows direct calls to React's built-in hooks, not transitively across files.
  • Styled-components is the only CSS-in-JS library currently detected as producing components.
  • Expect this plugin to be slower than path- or name-based import sorters. Classification requires reading and parsing each imported source file's AST (with per-run caching to amortise the cost), so the wall-clock format time is fundamentally bounded by disk I/O and Babel parse time rather than string comparison.

Contributing

See CONTRIBUTING for the contributing guide/information.

License

Copyright (c) 2026 Andrew Hathaway. Licensed under MIT license, see LICENSE for the full license.

Contact

You can find me on my website, Mastodon, and Bluesky.