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

@zhoumutou/postcss-wrap-layer

v0.1.2

Published

PostCSS plugin that wraps top-level CSS nodes into @layer blocks with rule-based matching, insertion strategies, merging, and virtual (no-path) content support.

Readme

@zhoumutou/postcss-wrap-layer

npm version weekly downloads license unpacked size

将顶层“裸露”的 CSS 节点按规则自动包裹进指定的 @layer。支持基于规则的匹配、灵活的插入位置、可选与现有同名 layer 合并以及幂等安全。

English | 中文

功能特性

  • 声明式规则:按文件路径或虚拟内容映射到 layer 名称
  • 灵活插入位置:endstartafter-ignoredpreserve
  • 可选与已存在的同名顶层 @layer 合并(合并时忽略插入位置,直接追加)
  • 若文件已完全包裹则跳过,幂等安全
  • 支持无真实路径(虚拟 / 内存)CSS,通过 contentTest 判断
  • 正则安全处理(剥离 g / y)与 TypeScript 友好

安装

npm i -D @zhoumutou/postcss-wrap-layer
# 或
pnpm add -D @zhoumutou/postcss-wrap-layer
# 或
yarn add -D @zhoumutou/postcss-wrap-layer

快速开始

// postcss.config.ts
import PostcssWrapLayer from '@zhoumutou/postcss-wrap-layer'

export default {
  plugins: [
    PostcssWrapLayer({
      rules: [
        {
          pattern: /components\.css$/,
          layerName: 'components',
          insertPosition: 'start',
        },
      ],
    }),
  ],
}

输入 (components.css):

.button { padding: 4px }

输出:

@layer components {
  .button { padding: 4px }
}

配置说明

/**
 * 插件选项(均为可选)。
 */
interface PluginOptions {
  /**
   * 有序的包裹规则;为空或缺省则不处理。
   * 默认: []
   */
  rules?: Rule[]
  /**
   * 完整替换的忽略 at-rule 名称列表。
   * 这些名称的 at-rule 会保持在顶层,也用作插入定位锚点。
   * 默认: ['charset','import','layer']
   */
  ignoreNames?: string[]
  /**
   * 多规则匹配策略:
   *  - 'first': 仅使用第一个命中的规则
   *  - 'warn' : 警告一次后与 first 相同
   *  - 'all'  : 顺序全部应用;每个规则只看到“剩余”裸节点
   * 默认: 'first'
   */
  multiMatch?: 'first' | 'warn' | 'all'
  /**
   * 当满足:
   *  - 没有剩余裸节点
   *  - 且存在同名顶层 @layer
   * 时跳过包裹。
   * 默认: true
   */
  skipIfAlreadyWrapped?: boolean
  /**
   * 若为 true,若已存在同名顶层 @layer,则直接把裸节点追加进去,
   * 而不是创建新块(有助于保持原级联锚点)。
   * 发生合并时会忽略 insertPosition(总是追加到该 layer 末尾)。
   * 默认: true
   */
  mergeSameLayer?: boolean
}

/**
 * 单条包裹规则。
 */
interface Rule {
  /**
   * 仅在存在真实文件路径时测试的正则。
   * 内部会移除状态标志 g / y。
   */
  pattern: RegExp
  /**
   * 目标(或合并目标)@layer 名称。必须非空且不含换行或分号。
   */
  layerName: string
  /**
   * 新建 layer 的插入方式:
   *  - 'end' (默认): 追加在末尾
   *  - 'start'      : 紧随开头连续的 @charset/@import
   *  - 'after-ignored': 在最后一个 ignoreNames 列表内的 at-rule 之后
   *  - 'preserve'   : 放在被采集裸节点中最早出现的位置
   * 若发生合并 (mergeSameLayer) 则此值被忽略。
   */
  insertPosition?: 'end' | 'start' | 'after-ignored' | 'preserve'
  /**
   * 仅在无真实文件路径(虚拟 / 内存 CSS)时调用的判定函数。
   * 接收完整 CSS 文本(必要时序列化一次)。
   */
  contentTest?: (css: string) => boolean
}

示例:

// postcss.config.ts
import PostcssWrapLayer from '@zhoumutou/postcss-wrap-layer'

export default {
  plugins: [
    PostcssWrapLayer({
      multiMatch: 'all',
      rules: [
        { pattern: /global\.css$/, layerName: 'base', insertPosition: 'start' },
        { pattern: /global\.css$/, layerName: 'components', insertPosition: 'after-ignored' },
        { pattern: /virtual\.css$/, layerName: 'runtime', contentTest: css => css.includes('/*runtime*/') },
      ],
    })
  ]
}

工作原理

  1. 规范化与校验规则(剥离正则的 g / y 标志)。
  2. 若无真实路径且存在需要 contentTest 的规则,则序列化一次 CSS。
  3. 匹配规则:
    • 有真实路径 → 用 pattern 测试
    • 无路径 → 使用 contentTest
  4. 根据多匹配策略筛选应用的规则。
  5. 针对每条应用的规则:
    • 收集“裸”节点(顶层,且不在 ignoreNames 中)。
    • 若满足幂等跳过条件则忽略。
    • 若开启合并且已有同名 layer → 直接追加;否则创建新 layer。
    • 计算插入位置(preserve 使用最早的原始索引;若合并则忽略)。
    • 将裸节点移动进目标 layer。

裸节点定义:任意顶层非 @rule 节点,或名称不在 ignoreNames 列表中的 @rule。

使用提示

  • 使用 preserve 保持新 layer 靠近其原始首个节点的位置。
  • mergeSameLayer=true 将节点直接追加到已存在的同名 layer,忽略 insertPosition
  • 若希望生成多个同名 block(仍被 CSS 视为一个逻辑 layer),可关闭 mergeSameLayer
  • 想让特定 at-rule(如 font-face)保持顶层可加入 ignoreNames
  • 保持 contentTest 逻辑轻量;它只在无路径且有需要时执行一次。
  • 虚拟输入无法使用 pattern,要依赖 contentTest

边界情况

| 情况 | 结果 | | --------------------------------------------- | -------------------------------- | | 无规则 | 不做任何处理 | | layerName 非法(空 / 含换行 / 分号) | 警告并跳过 | | 已存在同名 layer + mergeSameLayer=true | 直接追加,忽略插入位置 | | 已存在同名 layer + mergeSameLayer=false | 新建另一个同名 layer 块 | | 无裸节点 | 不添加任何内容 | | multiMatch='all' 且 insertPosition='preserve' | 每次规则重新计算裸节点与插入位置 |

许可证

MIT