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

postcss-plugin-shared

v1.1.6

Published

Shared utilities for postcss-plugins monorepo packages.

Readme

postcss-plugin-shared

English | 简体中文

postcss-plugin-shared is a shared utilities package used across the postcss-plugins monorepo to avoid duplicated logic (option merging, selector blacklists, declaration dedupe, exclude matching, unit regexes, numeric helpers, etc.).

Goals: small, stable, side-effect free, and reusable. This package intentionally does not include specific unit-conversion formulas (e.g. rem→px); it only provides generic building blocks.

Install / Usage

Inside this repo (pnpm workspace)

Use a workspace dependency:

// packages/your-plugin/package.json
{
  "dependencies": {
    "postcss-plugin-shared": "workspace:^"
  }
}

Then import utilities in your plugin:

import { createExcludeMatcher, remRegex } from 'postcss-plugin-shared'

Outside this repo

If you publish this package to npm, install it normally:

pnpm add postcss-plugin-shared

This package declares postcss as a peer dependency (^8).

Exports

Entry: packages/postcss-plugin-shared/src/index.ts

  • mergeOptions: option merging based on defu (arrays use “override” strategy)
  • createConfigGetter: create a getConfig(options?) helper based on mergeOptions
  • toFixed: stable rounding helper (avoids -0/precision noise)
  • createUnitRegex: build a unit regex with configurable skip rules
  • remRegex / pxRegex: shared regexes for rem/px replacement (skips string literals, url(), var())
  • blacklistedSelector: selector blacklist matcher (string includes / RegExp match)
  • maybeBlacklistedSelector: like blacklistedSelector, but returns undefined for non-string selectors
  • createPropListMatcher: builds a property matcher from propList (supports *)
  • createAdvancedPropListMatcher: advanced prop matcher (wildcards + negation) for string[]
  • createExcludeMatcher: builds an exclude matcher from exclude (array or function)
  • createSelectorBlacklistMatcher: selector blacklist matcher with optional cache
  • declarationExists: checks whether a rule/decls already contains the same prop/value to avoid duplicates
  • walkAndReplaceValues: shared walker to replace declaration values and media params

API

mergeOptions(options, defaults)

Merges user options with defaults:

  • Object fields follow defu semantics (fallback to defaults when not provided in options)
  • Arrays are overridden: if both sides are arrays, the user array replaces the default array
import { mergeOptions } from 'postcss-plugin-shared'

interface Options {
  propList: string[]
  unitPrecision: number
}

const defaults: Options = { propList: ['*'], unitPrecision: 5 }
const resolved = mergeOptions<Options>({ propList: ['font-size'] }, defaults)
// resolved.propList === ['font-size']

createConfigGetter(defaults)

Creates a strongly-typed getConfig(options?) function:

import { createConfigGetter } from 'postcss-plugin-shared'

const defaultOptions = { rootValue: 16, propList: ['*'] as string[] }
export const getConfig = createConfigGetter(defaultOptions)

getConfig() // => defaultOptions
getConfig({ rootValue: 10 }) // => merged result

toFixed(number, precision)

Stable rounding helper:

  • returns 0 when number === 0
  • preserves sign (supports negative values)
  • uses Number.EPSILON to reduce floating-point edge cases
import { toFixed } from 'postcss-plugin-shared'

toFixed(1.005, 2) // 1.01
toFixed(0, 5) // 0

createUnitRegex(options)

Creates a global regex for unit replacement with configurable “skip” rules.

Notes:

  • capture group 1 is the numeric portion
  • defaults to skipping quoted strings, url(...) and var(...)
import { createUnitRegex } from 'postcss-plugin-shared'

const pxLike = createUnitRegex({ units: ['px', 'rpx'], ignoreCase: true })

remRegex / pxRegex

Global regexes for String.prototype.replace, designed to reduce false positives:

  • skip double-quoted strings "..." and single-quoted strings '...'
  • skip url(...)
  • skip var(...)
  • capture group 1 is the numeric portion (e.g. 1.25)
import { remRegex } from 'postcss-plugin-shared'

const value = 'margin: 1rem 0; background: url("1rem.png")'
value.replace(remRegex, (m, num) => `${Number(num) * 16}px`)
// => margin: 16px 0; background: url("1rem.png")

blacklistedSelector(blacklist, selector?)

Returns true if selector matches the blacklist:

  • blacklist supports string | RegExp
  • returns false when selector is not a string
  • string: selector.includes(rule)
  • RegExp: Boolean(selector.match(rule))
import { blacklistedSelector } from 'postcss-plugin-shared'

blacklistedSelector(['.ignore', /^\.no-/], '.ignore .a') // true
blacklistedSelector(['.ignore', /^\.no-/], '.no-test') // true

maybeBlacklistedSelector(blacklist, selector?)

Same matching logic as blacklistedSelector, but returns undefined when selector is not a string.

createPropListMatcher(propList)

Builds a matcher (prop: string) => boolean to decide whether a CSS property should be processed.

Rules:

  • If propList includes '*', it matches everything.
  • String entries prefixed with ! exclude properties. Negated strings support !foo (exact) and glob patterns like !foo*, !*foo, !*foo*, !--wot-*-font-size.
  • Otherwise:
    • string without *: prop.includes(rule)
    • string with *: glob matching
    • RegExp: Boolean(prop.match(rule))
import { createPropListMatcher } from 'postcss-plugin-shared'

const match = createPropListMatcher(['font', /height$/])
match('font-size') // true (contains 'font')
match('line-height') // true (/height$/)
match('color') // false

const matchWithExcludes = createPropListMatcher(['*', '!font-size', '!padding*'])
matchWithExcludes('font-size') // false
matchWithExcludes('padding-right') // false

const matchCustomProps = createPropListMatcher(['*', '!--wot-*-font-size'])
matchCustomProps('--wot-body-font-size') // false

createAdvancedPropListMatcher(propList)

Advanced property matcher for string[] propList, compatible with postcss-pxtrans patterns:

  • * matches all properties
  • foo exact match
  • strings containing * use glob matching, such as foo*, *foo, *foo*, !--wot-*-font-size
  • !pattern negates (deny-list)
import { createAdvancedPropListMatcher } from 'postcss-plugin-shared'

const match = createAdvancedPropListMatcher(['*', '!border', 'font*', '*height'])
match('font-size') // true
match('border') // false

createExcludeMatcher(exclude)

Builds an exclude matcher (filepath?: string) => boolean.

  • exclude can be Array<string | RegExp> or (filePath) => boolean
  • returns false when filepath is undefined
import { createExcludeMatcher } from 'postcss-plugin-shared'

const isExcluded = createExcludeMatcher([/node_modules/i, 'vendor'])
isExcluded('/a/node_modules/x.css') // true
isExcluded('/a/src/vendor.css') // true

declarationExists(decls, prop, value)

Checks whether a rule already contains the same declaration (commonly used to avoid duplicates when replace: false and cloneAfter is used).

decls only needs a .some(...) method that iterates PostCSS ChildNodes (usually a Rule).

import { declarationExists } from 'postcss-plugin-shared'

// inside PostCSS visitor
if (!declarationExists(rule, decl.prop, nextValue)) {
  decl.cloneAfter({ value: nextValue })
}

Development

This package uses tsdown:

  • pnpm -C packages/postcss-plugin-shared dev
  • pnpm -C packages/postcss-plugin-shared build

Use cases

This package currently powers multiple plugins in this monorepo, e.g.:

  • postcss-rem-to-responsive-pixel
  • postcss-rem-to-viewport
  • postcss-pxtrans

If you want to extract more shared logic, this is the recommended place. Keep the scope:

  • utilities only (no plugin state, no IO, no side effects)
  • decoupled from conversion formulas (each plugin can define its own conversion)