Git Product home page Git Product logo

unplugin-preprocessor-directives's Introduction

logo

unplugin-preprocessor-directives

npm version npm downloads github stars bundle License JSDocs

English | 简体中文

Important

If you like this project, please consider giving it a star ⭐️. Your support will help this project become a part of the unplugin organization!

Install

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

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

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

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

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

unplugin-preprocessor-directives's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

unplugin-preprocessor-directives's Issues

This plugin breaks vitest coverage report

When using this plugin and adding // #v-ifdef DEV and // #v-endif comments in my code, vitest marks all code as 100% covered (istanbul).

image

If I remove the directives, the coverage works again:

image

Similar issues can be found at:

  1. unplugin/unplugin-vue-components#333
  2. unplugin/unplugin-vue-components#219 (maybe related, this is referred to in the previous link)

This should be easily reproducible.

Here is a minimal reproduction: https://stackblitz.com/edit/vitest-dev-vitest-4tlh7n?file=src%2Findex.ts

Is there a workaround to solve this problem?

conditions in html

I am trying to use this great plugin with vite. In typescript, it works well:

// #if DEV
console.log('DEV')
// #else
console.log('else DEV');
// #endif

but in html, with any combinations of whitespaces and # it doesn't work:

<!-- if DEV -->
    <div>DEV</div>
<!-- else -->
    <div>else DEV</div>
<!-- endif -->
<!-- #if DEV -->
    <div>#DEV</div>
<!-- #else -->
    <div>else #DEV</div>
<!-- #endif -->

It writes if and else branches.
It seems like plugin is not active, but it works on typescript.
I tried to write some code hacks to plugin, to log if my html file is proceeded.
I tried to use "include" option, but nothing works.
Please can you tell, what I am doing wrong?
Thank you...

Conflict with app.config.compilerOptions.whitespace = 'preserve', lost whitespace.

In Vue SFC file, with the following code

<template>
  <jy-form-dialog ref="form" v-model:entity="entity" v-bind="options" max-col="4">
        <jy-form-item label="输入控件配置说明" col="2">
          <pre>
{
  "tag": "jy-dict", //
  "data": {
    "props": {
      "type": "radio", // 显示类型:radio、单选按钮;checkbox、多选按钮;其他取值为下拉框
      "dict": "Common_YesNo", // 字典编码或者字典项列表如:["所属机构", "系统名称", "不显示名称" ]
      "dataType": "String" // 如果取值为字符串,需要指定为为String,否则会当做数值类型处理
    }
  }
}</pre
          >
        </jy-form-item>
  </jy-form-dialog>
</template>

<script>
import {defineComponent} from 'vue'

export default defineComponent({
  name: 'SettingDetail'
})
</script>

And in vite.config.ts file, use this plugin

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import PreprocessorDirectives from 'unplugin-preprocessor-directives/vite'

// https://vitejs.dev/config/
export default defineConfig((configEnv:any) => {
  return {
    plugins: [PreprocessorDirectives({ /* options */ }), vue()], // with PreprocessorDirectives plugin, the pre tag display not ok
    // plugins: [vue()] // use this line, the pre tag display ok
  }
})

I want to keep the whitespace in pre tag content for indentation display, if use PreprocessorDirectives plugin, the indentation lost.

I used following versions:
"vue": "^3.4.24",
"@vitejs/plugin-vue": "^5.0.4",
"unplugin-preprocessor-directives": "^1.0.3",
"vite": "^5.2.10",

In vue-cli5, plugin will conflict with html-webpack-plugin.

When I using this plugin with vue-cli5, vue-cli-service serve and vue-cli-service build will throw error:

Error: Child compilation failed:
  Module parse failed: Unexpected token (1:0)
  File was processed with these loaders:
   * ./node_modules/.pnpm/[email protected][email protected]/node_modules/html-webpack-plugin/lib/loader.js
   * ./node_modules/.pnpm/[email protected]/node_modules/unplugin/dist/webpack/loaders/transform.js
  You may need an additional loader to handle the result of these loaders.
  > <!DOCTYPE html>
  | <html lang="">
  |   <head>

I found html-webpack-plugin/lib/loader.js will check if there is any other loader, if exists it will return unmodified source, which cause error.
After some dig, I found add transformInclude hook and /\.html/ to plugin exclude config, unplugin will not create the loader which conflict with html-webpack-plugin and solve issue. But it also mean this plugin cant process html code. Maybe there is better way to fix this?

Sourcemaps always report position 0

In this function the sourcemap positions are always returned as position 0. This breaks mapping back to the original source and coverage reporting.

transformWithMap(code: string, _id: string) {

  transformWithMap(code: string, _id: string) {
    const generated = this.transform(code, _id)
    if (generated) {
      const ms = new MagicString(code, { filename: _id })
      ms.overwrite(0, code.length, generated)
      return {
        code: ms.toString(),
        map: ms.generateMap({ hires: true }),
      }
    }
  }

A simpler reproducible example (effectively the same code):

import MagicString from 'magic-string';

const code = `const answer = 42;\n\nconsole.log("The answer is", answer);`;
const generated = code;

const ms = new MagicString(code, { filename: 'test.ts' })
ms.overwrite(0, code.length, generated)
console.log(ms.generateMap({ hires: true }));

Returns:

SourceMap {
  version: 3,
  file: undefined,
  sources: [ '' ],
  sourcesContent: undefined,
  names: [],
  mappings: 'AAAA;AAAA'
}

AAAA is position 0 for both statements.

Import error with webpack

node:internal/modules/esm/resolve:853
  throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);
        ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'vite' imported from /Volumes/Extra/Code/conditional/node_modules/.pnpm/[email protected]/node_modules/unplugin-preprocessor-directives/dist/chunk-C3KB5XGL.js
    at packageResolve (node:internal/modules/esm/resolve:853:9)
    at moduleResolve (node:internal/modules/esm/resolve:910:20)
    at defaultResolve (node:internal/modules/esm/resolve:1130:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:396:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:365:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:240:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
    at link (node:internal/modules/esm/module_job:84:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Node.js v20.11.1
 ELIFECYCLE  Command failed with exit code 1.
       Error The "beforeDevCommand" terminated with a non-zero status code.

nextjs config:

/** @type {import('next').NextConfig} */
import PreprocessorDirectives from 'unplugin-preprocessor-directives/webpack'

const nextConfig = {
    output: 'export',
    webpack: (config) => {
        config.plugins = config.plugins || []
        config.plugins.push(new PreprocessorDirectives({}))
        return config
    }
};

export default nextConfig;

Cannot read properties of undefined (reading 'match') error when using comments without space before text

Hi,
after upgrading to version 1.0.0 (and also the latest 1.0.2) I'm getting the following error using the vite plugin (unplugin-preprocessor-directives/vite)

Cannot read properties of undefined (reading 'match')

After some testing, I found that this error seems to occur if a javascript comment does not contain a space after "//".
This comment creates an error : "//error comment", while this works without any issue "// working comment".
I've also created a playground demonstrating the issue at stackblitz:
https://stackblitz.com/edit/vitejs-vite-hy77nf?file=package.json,src%2FApp.vue
(Comment is in App.vue)

Thanks.

Edit: After rechecking stackblitz with Firefox I get an erorr saying "comment is undefined", Chrome shows the error above.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.