Awesome
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
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
:
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:
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
andattributes
the last is an object that represent the attributes of the style element that should have)
This is an example:
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
andattributes
the last is an object that represent the attributes of the style element that should have)
This is an example:
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
:
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:
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:
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:
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:
<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.
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
export default {
plugins: [
cssInjectedByJsPlugin({styleId: () => `foo-${Math.random() * 100}`}),
]
}
<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:
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 for
more information.
This is an example:
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 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. Since the plugin-legacy injects the CSS code for different reasons, 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.