English | 简体中文
npm i unplugin-preprocessor-directivesNote
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 */ }),
],
}The plugin accepts the following configuration options:
- 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()- Type:
string - Default:
process.cwd()
The current working directory. Used for resolving relative paths in #include directives.
- Type:
string | RegExp | (string | RegExp)[] - Default:
['**/*']
Files to include for processing. Supports glob patterns.
- Type:
string | RegExp | (string | RegExp)[] - Default:
[/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/]
Files to exclude from processing. Supports glob patterns.
- Type:
Directive[] - Default:
[]
Custom directives to add beyond the built-in ones. See Custom directive for more information.
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#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')
// #endifYou 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
}
}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 messageOf course, it can also be combined with conditional compilation:
// #if DEBUG
// #info Debug mode is on
// #endif
// #if !DEBUG
// #info Debug mode is off
// #endifYou 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
importorrequire- 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
- 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
- Path resolution: Relative paths are resolved relative to the configured working directory (
cwd) - File extensions: Any type of text file can be included, not limited to
.jsfiles - Nested processing: Included files are fully processed by the preprocessor, so all supported directives can be used
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}`
},
}))Execution priority of directives
pre: Execute as early as possiblepost: Execute as late as possible