Anyone who has used uni-app knows that it has built-in preprocess, which implements the ability to exclude code that doesn't belong to the current compilation platform via annotations such as #ifdef
, #ifndef
, and so on, thus reducing the size of the built package.
So how can we provide Vite with a similar capability?
Thinking
The simplest implementation of /#ifn?def/
is centered around two things: environment variable reading and text replacement.
Environment variables can be read directly from process.env
, but this is not safe by default, so we choose to read config.env
from the configResolved
hook.
Text Replacement is just a matter of matching and replacing the incoming code with a regular in the transform
hook. But this regular needs to match the keywords and conditions in the comments of any language.
Turning on regex101 gives you the following regular.
const reg = /^.*?#v-if(n?)def\s*(\S+).*[\r\n]{1,2}([\s\S]+?)\s*.*?#v-endif.*?$/gm
See visualization on Regex Vis
- The first grouping
(n?)
is used to determine if it is an inverse pattern or not - Second grouping
(\S+). *
Used to capture conditions - Third grouping
([\s\S]+?)
Used to capture the contents of the conditional compilation block.
Implementation
First, the classic plugin definition, where config is promoted to the next level for ease of use everywhere.
import type { Plugin, ResolvedConfig } from "vite";
let config: ResolvedConfig = undefined!;
const replaceMatched = (code: string, _id: string) => {}
const VitePluginConditionalCompile = (): Plugin => {
return {
name: "vite-plugin-conditional-compile",
enforce: "pre",
configResolved(_config) {
config = _config;
},
transform(code, id) {
return replaceMatched(code, id);
},
};
};
export default VitePluginConditionalCompile;
Next, focus on the replaceMatched function
const replaceMatched = (code: string, _id: string) => {
const env = config.env;
code = code.replace(
/^.*?#v-if(n?)def\s*(\S+).*[\r\n]{1,2}([\s\S]+?)\s*.*?#v-endif.*?$/gm,
(_, $1, $2, $3) => {
const isNot = !!$1;
const isKeep = $2.split("||").some((v: string) => {
let flag = false;
const [key, value] = v.split("=");
if (value === undefined) {
flag = !!env[key];
} else {
flag = String(env[key]) === value;
}
flag = isNot ? !flag : flag;
return flag;
});
return isKeep ? $3 : "";
}
);
return {
code,
map: null,
};
};
The first is const env = config.env;
which gets the environment variables that vite provides to the application (non-built-in environment variables need to start with VITE_
).
Then pinch, is the second function parameter of the core replace, $1
, $2
, $3
for three groups, isNot
according to the presence or absence of n
to determine whether it is an inverse pattern, in order to support the condition ||
(or), isKeep
will be the condition part of the use of split
to split, and then use some
(i.e., true
if one is true
) to judge the single condition inside some
, and finally keep isKeep
if it is true
, and discard it otherwise.
(_, $1, $2, $3) => {
const isNot = !!$1;
const isKeep = $2.split("||").some((v: string) => {
let flag = false;
const [key, value] = v.split("=");
if (value === undefined) {
flag = !!env[key];
} else {
flag = String(env[key]) === value;
}
flag = isNot ? !flag : flag;
return flag;
});
return isKeep ? $3 : "";
}
Use
The plugin I've uploaded to KeJunMao/vite-plugin-conditional-compile is exceptionally easy to use!
// vite.config.ts
import { defineConfig } from "vite";
import ConditionalCompile from "vite-plugin-conditional-compiler";
export default defineConfig({
plugins: [ConditionalCompile()],
});
Question.
This plugin already does most of the conditional compilation capabilities, but there are still a couple of issues:
- Nesting is not supported
- no support for else, elif
- No support for && or more complex expressions.
Though I did support the else directive at a later stage.
Finally, if you have a need for conditional compilation, I no longer recommend this plugin, but rather KeJunMao/unplugin-preprocessor-directives.
It uses unplugin to work with any major packager, and secondly conditional compilation takes care of all the above issues.
Most importantly, this plugin abstracts the directives into plugins for plugins. In addition to the built-in conditional compilation directives, it also supports defining, undefining, and printing information at the line of code (at compile time).
You can also customize the directives , yes , if you want , you can even define foreach, include and other directives .
If it's useful to you, give it a like before you go~
Top comments (0)