# unplugin-preprocessor-directives

Latest version **1.2.0** (published 2025-11-05) · MIT license · 0 weekly downloads

## Install

```sh
npm install unplugin-preprocessor-directives
pnpm add unplugin-preprocessor-directives
yarn add unplugin-preprocessor-directives
bun add unplugin-preprocessor-directives
```

## Health

**Score 60/100 (C)** — status: stable.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.2.0 |
| Published | 2025-11-05 |
| First published | 2023-09-05 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 5 |
| Unpacked size | 240.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 131 |
| Maintainers | kejunmao |
| Keywords | unplugin, vite, webpack, rollup, rspack, transform, vite-plugin, webpack-plugin, rollup-plugin, esbuild-plugin, rspack-plugin, nuxt-plugin, directives, preprocessor |

## Links

- npm: https://www.npmjs.com/package/unplugin-preprocessor-directives
- Repository: https://github.com/kejunmao/unplugin-preprocessor-directives
- Homepage: https://github.com/kejunmao/unplugin-preprocessor-directives#readme
- Issues: https://github.com/kejunmao/unplugin-preprocessor-directives/issues
- npm.io page: https://npm.io/package/unplugin-preprocessor-directives

## Dependencies (5)

- [unplugin](https://npm.io/package/unplugin.md) ^2.3.9
- [picocolors](https://npm.io/package/picocolors.md) ^1.1.1
- [magic-string](https://npm.io/package/magic-string.md) ^0.30.18
- [dotenv-expand](https://npm.io/package/dotenv-expand.md) ^12.0.3
- [@jridgewell/remapping](https://npm.io/package/@jridgewell/remapping.md) ^2.3.5

## Alternatives

- [raw-loader](https://npm.io/package/raw-loader.md) — 4.3M weekly downloads
- [plop](https://npm.io/package/plop.md) — 1.4M weekly downloads
- [webpack-deadcode-plugin](https://npm.io/package/webpack-deadcode-plugin.md) — 80.3K weekly downloads
- [@storybook/preact-vite](https://npm.io/package/@storybook/preact-vite.md) — 54.2K weekly downloads
- [vite-plugin-transform](https://npm.io/package/vite-plugin-transform.md) — 2.4K weekly downloads

## Recent versions

- 1.2.0 (latest) — 2025-11-05
- 1.1.0 — 2025-11-02
- 1.0.4 — 2025-09-17
- 1.0.3 — 2024-01-27
- 1.0.2 — 2024-01-26
- 1.0.1 — 2024-01-21
- 1.0.0 — 2024-01-21
- 0.0.8 — 2023-10-19
- 0.0.7 — 2023-09-28
- 0.0.6 — 2023-09-28
- 0.0.5 — 2023-09-20
- 0.0.4 — 2023-09-18
- 0.0.3 — 2023-09-13
- 0.0.2 — 2023-09-06
- 0.0.1 — 2023-09-05

## README

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

# unplugin-preprocessor-directives

[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![github stars][github-stars-src]][github-stars-href]
[![bundle][bundle-src]][bundle-href]
[![License][license-src]][license-href]
[![JSDocs][jsdocs-src]][jsdocs-href]

English | [简体中文](./README.zh-cn.md)

## Install

```bash
npm i unplugin-preprocessor-directives
```

> [!NOTE]
> This plugin should be placed **before** all other plugins in your configuration to ensure preprocessor directives are processed first.

<details>
<summary>Vite</summary><br>

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

export default defineConfig({
  plugins: [
    PreprocessorDirectives({ /* options */ }), // Should be the first plugin
  ],
})
```

Example: [`playground/`](./playground/)

<br></details>

<details>
<summary>Rollup</summary><br>

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

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

<br></details>

<details>
<summary>Webpack</summary><br>

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

<br></details>

<details>
<summary>Nuxt</summary><br>

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

> This module works for both Nuxt 2 and [Nuxt Vite](https://github.com/nuxt/vite)

<br></details>

<details>
<summary>Vue CLI</summary><br>

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

<br></details>

<details>
<summary>esbuild</summary><br>

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

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

<br></details>

<details>
<summary>Rspack (⚠️ experimental)</summary><br>

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

<br></details>

## Usage

### Defining symbols

You use the following two preprocessor directives to define or undefine symbols for conditional compilation:

- `#define`: Define a symbol.
- `#undef`: Undefine a symbol.

You use `#define` to define a symbol. When you use the symbol as the expression that's passed to the `#if` directive, the expression will evaluate to `true`, as the following example shows:

```ts
// #define VERBOSE

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

### Conditional compilation

- `#if`: Opens a conditional compilation, where code is compiled only if the specified symbol is defined and evaluated to true.
- `#elif`: Closes the preceding conditional compilation and opens a new conditional compilation based on if the specified symbol is defined and evaluated to true.
- `#else`: Closes the preceding conditional compilation and opens a new conditional compilation if the previous specified symbol isn't defined or evaluated to false.
- `#endif`: Closes the preceding conditional compilation.

> [!NOTE]
> By default, use vite's `loadEnv` function to load environment variables based on `process.env.NODE_ENV` and compile symbols as conditions.

```ts
// src/index.ts

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

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

You can use the operators `==` (equality) and `!=` (inequality) to test for the bool values `true` or `false`. `true` means the symbol is defined. The statement `#if DEBUG` has the same meaning as `#if (DEBUG == true)`. You can use the `&&` (and), `||` (or), and `!` (not) operators to evaluate whether multiple symbols have been defined. You can also group symbols and operators with parentheses.

```ts
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 and warning and info messages

You instruct the compiler to generate user-defined compiler errors and warnings and informational messages.

- `#error`: Generates an error, but does not terminate compilation.
- `#warning`: Generates a warning.
- `#info`: Generates an informational message.

```ts
// #error this is an error message
// #warning this is a warning message
// #info this is an info message
```

Of course, it can also be combined with conditional compilation:

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

### `#include` directive

You can use the `#include` directive to include the contents of other files into the current file. The included files are also processed by the preprocessor.

> [!WARNING]
> The `#include` directive is a **compile-time text replacement tool**, primarily intended for these scenarios:
> - Including different configuration code snippets in different environments
> - Combining with conditional compilation to include different code based on compilation conditions
> - Sharing code snippets that require preprocessing
>
> **It cannot and should not replace:**
> - JavaScript/TypeScript `import` or `require` - for modularization and dependency management
> - CSS `@import` - for stylesheet modularization
> - HTML template systems or component systems
>
> If you simply want to modularize your code, please use the language's native module system. Only use `#include` when you need compile-time processing and conditional inclusion.

this directive supports the following two syntaxes:

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

> [!NOTE]
> 1. **Circular references**: If file A includes file B, and file B includes file A, circular references will be automatically detected and prevented, processing only once
> 2. **Path resolution**: Relative paths are resolved relative to the configured working directory (`cwd`)
> 3. **File extensions**: Any type of text file can be included, not limited to `.js` files
> 4. **Nested processing**: Included files are fully processed by the preprocessor, so all supported directives can be used

## Custom directive

You can used `defineDirective` to define your own directive.

Taking the built-in directive as an example:

```ts
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'`

Execution priority of directives

- `pre`: Execute as early as possible
- `post`: Execute as late as possible

[npm-version-src]: https://img.shields.io/npm/v/unplugin-preprocessor-directives?style=flat&colorA=18181B&colorB=F0DB4F
[npm-version-href]: https://npmjs.com/package/unplugin-preprocessor-directives
[npm-downloads-src]: https://img.shields.io/npm/dw/unplugin-preprocessor-directives?style=flat&colorA=18181B&colorB=F0DB4F
[npm-downloads-href]: https://npmjs.com/package/unplugin-preprocessor-directives
[github-stars-src]: https://img.shields.io/github/stars/kejunmao/unplugin-preprocessor-directives?style=flat&colorA=18181B&colorB=F0DB4F
[github-stars-href]: https://github.com/kejunmao/unplugin-preprocessor-directives
[bundle-src]: https://img.shields.io/bundlephobia/minzip/unplugin-preprocessor-directives?style=flat&colorA=18181B&colorB=F0DB4F
[bundle-href]: https://bundlephobia.com/result?p=unplugin-preprocessor-directives
[license-src]: https://img.shields.io/github/license/kejunmao/unplugin-preprocessor-directives.svg?style=flat&colorA=18181B&colorB=F0DB4F
[license-href]: https://github.com/kejunmao/unplugin-preprocessor-directives/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsDocs.io-reference-18181B?style=flat&colorA=18181B&colorB=F0DB4F
[jsdocs-href]: https://www.jsdocs.io/package/unplugin-preprocessor-directives

---
_Source: https://npm.io/package/unplugin-preprocessor-directives · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
