Skip to content

codota/unplugin-preprocessor-directives

 
 

Repository files navigation

logo

unplugin-preprocessor-directives

npm version npm downloads github stars bundle License JSDocs

English | 简体中文

Install

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.

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

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

Example: playground/


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

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


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


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

This module works for both Nuxt 2 and Nuxt Vite


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


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

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


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


Usage

Plugin Options

The plugin accepts the following configuration options:

preserveLineNumbers

  • Type: boolean
  • Default: false

When enabled, the plugin will preserve line numbers by replacing removed code with empty lines instead of actually removing it. This is useful for debugging as it ensures that line numbers in error messages and debugger match the original source code.

PreprocessorDirectives({
  preserveLineNumbers: true,
})

Example:

Original code:

const message = 'Hello'

// #if DEV

console.log('Development mode')

// #endif

const result = compute()

With preserveLineNumbers: false (default):

const message = 'Hello'

const result = compute()

With preserveLineNumbers: true:

const message = 'Hello'

const result = compute()

cwd

  • Type: string
  • Default: process.cwd()

The current working directory. Used for resolving relative paths in #include directives.

include

  • Type: string | RegExp | (string | RegExp)[]
  • Default: ['**/*']

Files to include for processing. Supports glob patterns.

exclude

  • Type: string | RegExp | (string | RegExp)[]
  • Default: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/]

Files to exclude from processing. Supports glob patterns.

directives

  • Type: Directive[]
  • Default: []

Custom directives to add beyond the built-in ones. See Custom directive for more information.

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:

// #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.

// 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.

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.
// #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:

// #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:

// #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:

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

About

preprocessor directives for jsx,tsx,js,ts,html,css,vue and more

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 98.7%
  • HTML 1.1%
  • JavaScript 0.2%