366 lines
12 KiB
Markdown
366 lines
12 KiB
Markdown
# vite-plugin-css-injected-by-js 🤯
|
|
|
|
A Vite plugin that takes the CSS and adds it to the page through the JS. For those who want a single JS file.
|
|
|
|
The plugin can be configured to execute the CSS injection before or after your app code, and you can also provide a
|
|
custom id for the injected style element and other configurations that fulfill some particular cases, even for libs.
|
|
|
|
## How does it work
|
|
|
|
Essentially what it does is take all the CSS generated by the build process and add it via JavaScript. The CSS file is
|
|
therefore not generated and the declaration in the generated HTML file is also removed. You can also configure when the
|
|
CSS injection will be executed (before or after your app code).
|
|
|
|
## Installation
|
|
|
|
```
|
|
npm i vite-plugin-css-injected-by-js --save
|
|
```
|
|
|
|
## Usage
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin(),
|
|
]
|
|
}
|
|
```
|
|
|
|
### Configurations
|
|
|
|
When you add the plugin, you can provide a configuration object. Below you can find all configuration parameters
|
|
available.
|
|
|
|
#### cssAssetsFilterFunction (function)
|
|
|
|
The `cssAssetsFilterFunction` parameter allows you to specify a filter function that will enable you to exclude some
|
|
output css assets.
|
|
|
|
**This option is not applied to `relativeCSSInjection` logic.**
|
|
|
|
Here is an example of how to use the `cssAssetsFilterFunction`:
|
|
|
|
```javascript
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
cssAssetsFilterFunction: function customCssAssetsFilterFunction(outputAsset) {
|
|
return outputAsset.fileName == 'font.css';
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
#### dev (object)
|
|
|
|
**EXPERIMENTAL**
|
|
Why experimental? Because it uses a non-conventional solution.
|
|
|
|
Previously, the plugin strictly applied logic solely during the build phase. Now, we have the capability to experiment
|
|
with it in the development environment.
|
|
|
|
To activate the plugin in the development environment as well, you need to configure a dev object and set the enableDev
|
|
parameter to true.
|
|
|
|
Here's an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
dev: {
|
|
enableDev: true,
|
|
removeStyleCodeFunction: function removeStyleCode(id: string) {
|
|
// The 'id' corresponds to the value of the 'data-vite-dev-id' attribute found on the style element. This attribute is visible even when the development mode of this plugin is not activated.
|
|
}
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
This approach should serve its purpose effectively unless you're employing custom injection code to insert styles where
|
|
necessary. Since the development environment involves the concept of "updating" styles in the Document Object Model (
|
|
DOM), this plugin requires code to remove the injected style from the DOM.
|
|
|
|
Due to these factors, if you're utilizing custom injection code (via `injectCode` or `injectCodeFunction`), the plugin
|
|
cannot automatically discern how to delete the injected style. Therefore, all you need to do is configure
|
|
either `removeStyleCode` or `removeStyleCodeFunction` within the `dev` object as demonstrated above.
|
|
|
|
**NOTE:** The `injectCode` and `injectCodeFunction` parameters now also include the `attributes`, and in `dev` mode,
|
|
the `attributes` object encompasses the `data-vite-dev-id` as well. Refer to the `injectCodeFunction` example below for
|
|
further details.
|
|
|
|
#### injectCode (function)
|
|
|
|
You can provide also a function for `injectCode` param to customize the injection code used. The `injectCode` callback
|
|
must return a `string` (with valid JS code) and it's called with two arguments:
|
|
|
|
- cssCode (the `string` that contains all the css code that need to be injected via JavaScript)
|
|
- options (an object with `styleId`, `useStrictCSP` and `attributes` the last is an object that represent the attributes
|
|
of the style element that should have)
|
|
|
|
This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
injectCode: (cssCode: string, options: InjectCodeOptions) => {
|
|
return `try{if(typeof document != 'undefined'){var elementStyle = document.createElement('style');elementStyle.appendChild(document.createTextNode(${cssCode}));document.head.appendChild(elementStyle);}}catch(e){console.error('vite-plugin-css-injected-by-js', e);}`
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
#### injectCodeFunction (function)
|
|
|
|
If you prefer to specify the injectCode as a plain function you can use the `injectCodeFunction` param.
|
|
|
|
The `injectCodeFunction` function is a void function that will be called at runtime application with two arguments:
|
|
|
|
- cssCode (the `string` that contains all the css code that need to be injected via JavaScript)
|
|
- options (an object with `styleId`, `useStrictCSP` and `attributes` the last is an object that represent the attributes
|
|
of the style element that should have)
|
|
|
|
This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
injectCodeFunction: function injectCodeCustomRunTimeFunction(cssCode: string, options: InjectCodeOptions) {
|
|
try {
|
|
if (typeof document != 'undefined') {
|
|
var elementStyle = document.createElement('style');
|
|
|
|
// SET ALL ATTRIBUTES
|
|
for (const attribute in options.attributes) {
|
|
elementStyle.setAttribute(attribute, options.attributes[attribute]);
|
|
}
|
|
|
|
elementStyle.appendChild(document.createTextNode(${cssCode}));
|
|
document.head.appendChild(elementStyle);
|
|
}
|
|
} catch (e) {
|
|
console.error('vite-plugin-css-injected-by-js', e);
|
|
}
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
#### injectionCodeFormat (ModuleFormat)
|
|
|
|
You can specify the format of the injection code, by default is `iife`.
|
|
|
|
#### jsAssetsFilterFunction (function)
|
|
|
|
The `jsAssetsFilterFunction` parameter allows you to specify which JavaScript file(s) the CSS injection code should be
|
|
added to. This is useful when using a Vite configuration that exports multiple entry points in the building process. The
|
|
function takes in an OutputChunk object and should return true for the file(s) you wish to use as the host of the CSS
|
|
injection. If multiple files are specified, the CSS injection code will be added to all files returned as true.
|
|
|
|
Here is an example of how to use the `jsAssetsFilterFunction`:
|
|
|
|
```javascript
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
jsAssetsFilterFunction: function customJsAssetsfilterFunction(outputChunk) {
|
|
return outputChunk.fileName == 'index.js';
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
In this example, the CSS injection code will only be added to the `index.js` file. If you wish to add the code to
|
|
multiple files, you can specify them in the function:
|
|
|
|
```javascript
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({
|
|
jsAssetsFilterFunction: function customJsAssetsfilterFunction(outputChunk) {
|
|
return outputChunk.fileName == 'index.js' || outputChunk.fileName == 'subdir/main.js';
|
|
}
|
|
}),
|
|
]
|
|
}
|
|
```
|
|
|
|
This code will add the injection code to both index.js and main.js files.
|
|
**Be aware that if you specified multiple files that the CSS can be doubled.**
|
|
|
|
#### preRenderCSSCode (function)
|
|
|
|
You can use the `preRenderCSSCode` parameter to make specific changes to your CSS before it is printed in the output JS
|
|
file. This parameter takes the CSS code extracted from the build process and allows you to return the modified CSS code
|
|
to be used within the injection code.
|
|
|
|
This way, you can customize the CSS code without having to write additional code that runs during the execution of your
|
|
application.
|
|
|
|
This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({preRenderCSSCode: (cssCode) => cssCode}), // The return will be used as the CSS that will be injected during execution.
|
|
]
|
|
}
|
|
```
|
|
|
|
#### relativeCSSInjection (boolean)
|
|
|
|
_This feature is based on information provided by Vite. Since we can't control how Vite handles this information this
|
|
means that there may be problems that may not be possible to fix them in this plugin._
|
|
|
|
The default behavior of this plugin takes all the CSS code of your application directly to the entrypoint generated.
|
|
The `relativeCSSInjection` if configured to `true` will inject the CSS code of every entrypoint to the relative
|
|
importer.
|
|
|
|
**Set this option to `true` if you are using the multiple entry point option of Rollup.**
|
|
**For this feature to work, `build.cssCodeSplit` must be set to `true`**
|
|
|
|
_Future release can have an advanced behavior where this options will be configured to true automatically by sniffing
|
|
user configurations._
|
|
|
|
If a CSS chunk is generated that's not imported by any JS chunk, a warning will be shown. To disable this warning
|
|
set `suppressUnusedCssWarning` to `true`.
|
|
|
|
#### styleId (string | function)
|
|
|
|
If you provide a `string` for `styleId` param, the code of injection will set the `id` attribute of the `style` element
|
|
with the value of the parameter provided. This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({styleId: "foo"}),
|
|
]
|
|
}
|
|
```
|
|
|
|
The output injected into the DOM will look like this example:
|
|
|
|
```html
|
|
|
|
<head>
|
|
<style id="foo">/* Generated CSS rules */</style>
|
|
</head>
|
|
```
|
|
|
|
If you provide a `function` for `styleId` param, it will run that function and return a string. It's especially useful
|
|
if you use `relativeCSSInjection` and want unique styleIds for each file.
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({styleId: () => `foo-${Math.random() * 100}`}),
|
|
]
|
|
}
|
|
```
|
|
|
|
```html
|
|
|
|
<head>
|
|
<style id="foo-1234">/* Generated CSS rules */</style>
|
|
<style id="foo-4321">/* Generated CSS rules */</style>
|
|
</head>
|
|
```
|
|
|
|
#### topExecutionPriority (boolean)
|
|
|
|
The default behavior adds the injection of CSS before your bundle code. If you provide `topExecutionPriority` equal
|
|
to: `false` the code of injection will be added after the bundle code. This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({topExecutionPriority: false}),
|
|
]
|
|
}
|
|
```
|
|
|
|
#### useStrictCSP (boolean)
|
|
|
|
The `useStrictCSP` configuration option adds a nonce to style tags based
|
|
on `<meta property="csp-nonce" content={{ nonce }} />`. See the following [link](https://cssinjs.org/csp/?v=v10.9.2) for
|
|
more information.
|
|
|
|
This is an example:
|
|
|
|
```ts
|
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
|
|
|
export default {
|
|
plugins: [
|
|
cssInjectedByJsPlugin({useStrictCSP: true}),
|
|
]
|
|
}
|
|
```
|
|
|
|
The tag `<meta property="csp-nonce" content={{ nonce }} />` (nonce should be replaced with the value) must be present in
|
|
your html page. The `content` value of that tag will be provided to the `nonce` property of the `style` element that
|
|
will be injected by our default injection code.
|
|
|
|
## Contributing
|
|
|
|
When you make changes to plugin locally, you may want to build the js from the typescript file of the plugin.
|
|
|
|
Here the guidelines:
|
|
|
|
### Install
|
|
|
|
```
|
|
npm install
|
|
```
|
|
|
|
### Testing
|
|
|
|
```
|
|
npm run test
|
|
```
|
|
|
|
### Build plugin
|
|
|
|
```
|
|
npm run build
|
|
```
|
|
|
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
|
|
|
|
### A note for plugin-legacy users
|
|
|
|
At first the plugin supported generating the CSS injection code also in the legacy files generated by
|
|
the [plugin-legacy](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy). Since the plugin-legacy injects
|
|
the CSS code for [different reasons](https://github.com/vitejs/vite/issues/2062), this plugin no longer has the
|
|
plugin-legacy support code. If the code of the plugin-legacy changes an update to this plugin may occur. |