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 🙏

© 2025 – Pkg Stats / Ryan Hefner

unplugin-preprocessor-directives

v1.2.0

Published

<img src="assets/logo.svg" alt="logo" width="100" height="100" align="right" />

Readme

unplugin-preprocessor-directives

npm version npm downloads bundle License JSDocs

English | 简体中文

安装

npm i unplugin-preprocessor-directives

[!IMPORTANT] 此插件应该放在配置中所有其他插件之前,以确保预处理器指令首先被处理。

// vite.config.ts
import PreprocessorDirectives from 'unplugin-preprocessor-directives/vite'

export default defineConfig({
  plugins: [
    PreprocessorDirectives({ /* options */ }), // 应该是第一个插件
  ],
})

Example: playground/

// rollup.config.js
import PreprocessorDirectives from 'unplugin-preprocessor-directives/rollup'

export default {
  plugins: [
    PreprocessorDirectives({ /* options */ }),
  ],
}

// webpack.config.js
module.exports = {
  /* ... */
  plugins: [
    require('unplugin-preprocessor-directives/webpack')({ /* options */ })
  ]
}

// nuxt.config.js
export default defineNuxtConfig({
  modules: [
    ['unplugin-preprocessor-directives/nuxt', { /* options */ }],
  ],
})

This module works for both Nuxt 2 and Nuxt Vite

// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      require('unplugin-preprocessor-directives/webpack')({ /* options */ }),
    ],
  },
}

// esbuild.config.js
import { build } from 'esbuild'
import PreprocessorDirectives from 'unplugin-preprocessor-directives/esbuild'

build({
  plugins: [PreprocessorDirectives()],
})

// rspack.config.js
module.exports = {
  plugins: [
    require('unplugin-preprocessor-directives/rspack')({ /* options */ }),
  ],
}

使用

定义 symbols

您可以使用以下两个预处理器指令来定义或取消定义 symbols,以便进行条件编译:

  • #define: 定义一个 symbol.
  • #undef: 取消定义一个 symbol.

使用 #define 可以定义一个 symbol。将 symbol 作为表达式传递给 #if 指令时,表达式的值将为 true,如下例所示:

// #define VERBOSE

// #if VERBOSE
console.log('Verbose output version')
// #endif

条件编译

  • #if: 打开条件编译,只有当指定的 symbol 被定义并求值为 true 时,代码才会被编译。
  • #elif:关闭前面的条件编译,并判断是否定义了指定的 symbol 并求值为 true 时,打开一个新的条件编译。
  • #else: 如果前一个指定的 symbol 未定义或求值为 false,则关闭前一个条件编译,并打开一个新的条件编译。
  • #endif: 关闭前面的条件编译。

[!NOTE] 默认情况下,使用 vite 的 loadEnv 函数根据process.env.NODE_ENV 加载环境变量并作为条件编译 symbols。

// src/index.ts

// #if DEV
console.log('Debug version')
// #endif

// #if !MYTEST
console.log('MYTEST is not defined or false')
// #endif

可以使用运算符 == (相等)和 != (不等)来测试 truefalsetrue 表示 symbol 已定义。语句 #if DEBUG#if (DEBUG == true) 意义相同。支持使用 && (与)、|| (或) 和 ! (非) 操作符来判断是否定义了多个 symbols。还可以用括号将 symbols 和运算符分组。

class MyClass {
  constructor() {
    // #if (DEBUG && MYTEST)
    console.log('DEBUG and MYTEST are defined')
    // #elif (DEBUG==false && !MYTEST)
    console.log('DEBUG and MYTEST are not defined')
    // #endif
  }
}

错误、警告和信息提示

可以指示编译器生成用户定义的编译器错误、警告和信息。

  • #error: 生成一条错误消息,但不会终止编译。
  • #warning: 生成一条警告消息。
  • #info: 生成一条信息消息。
// #error this is an error message
// #warning this is a warning message
// #info this is an info message

当然,也可以和条件编译结合使用:

// #if DEBUG
// #info Debug mode is on
// #endif
// #if !DEBUG
// #info Debug mode is off
// #endif

#include 指令

您可以使用 #include 指令将其他文件的内容包含到当前文件中。被包含的文件也会经过预处理器处理。

[!WARNING] #include 指令是一个编译时文本替换工具,主要用于以下场景:

  • 在不同环境下包含不同的配置代码片段
  • 与条件编译结合使用,根据编译条件包含不同的代码
  • 共享需要预处理的代码片段

它不能也不应该替代:

  • JavaScript/TypeScript 的 importrequire - 用于模块化和依赖管理
  • CSS 的 @import - 用于样式表的模块化
  • HTML 的模板系统或组件系统

如果您只是想要模块化代码,请使用语言原生的模块系统。只有在需要编译时处理和条件包含时才使用 #include

该指令支持以下两种语法:

// #include "path/to/file"
or
// #include <path/to/file>

[!NOTE]

  1. 循环引用: 如果文件 A 包含文件 B,而文件 B 又包含文件 A,会自动检测并阻止循环引用,只处理一次
  2. 路径解析: 相对路径是相对于配置的工作目录(cwd)解析的
  3. 文件扩展名: 可以包含任何类型的文本文件,不限于 .js 文件
  4. 嵌套处理: 包含的文件会完整地通过预处理器,所以可以使用所有支持的指令

自定义指令

您可以使用 defineDirective 定义自己的指令。

以内置指令为例:

export const MessageDirective = defineDirective<MessageToken, MessageStatement>(context => ({
  lex(comment) {
    return simpleMatchToken(comment, /#(error|warning|info)\s*(.*)/)
  },
  parse(token) {
    if (token.type === 'error' || token.type === 'warning' || token.type === 'info') {
      this.current++
      return {
        type: 'MessageStatement',
        kind: token.type,
        value: token.value,
      }
    }
  },
  transform(node) {
    if (node.type === 'MessageStatement') {
      switch (node.kind) {
        case 'error':
          context.logger.error(node.value, { timestamp: true })
          break
        case 'warning':
          context.logger.warn(node.value, { timestamp: true })
          break
        case 'info':
          context.logger.info(node.value, { timestamp: true })
          break
      }
      return createProgramNode()
    }
  },
  generate(node, comment) {
    if (node.type === 'MessageStatement' && comment)
      return `${comment.start} #${node.kind} ${node.value} ${comment.end}`
  },
}))

enforce: 'pre' | 'post'

指令的执行优先级

  • pre 尽可能早执行
  • post 尽可能晚执行