npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-inject-scripts

v1.1.0

Published

A Vite plugin that injects `<script>` tags into the generated HTML files

Readme

vite-plugin-inject-scripts

npm GitHub License: MIT

A Vite plugin that injects <script> tags into the generated HTML files.

Overview

This plugin lets you declare a list of "prior" scripts and decide exactly where and how each one is inserted into every (or selected) HTML file:

  • Inline the script content or reference it via src;
  • Place it at the beginning/end of <head> or <body>;
  • Set a type (module, importmap, speculationrules, IIFE, or a custom value);
  • Pass through arbitrary attributes (async, defer, crossorigin, nonce, …);
  • Restrict injection to specific HTML files;
  • Transform TypeScript sources on the fly.

It works both during development (vite serve) and at build time (vite build).

Installation

# npm
npm install --save-dev vite-plugin-inject-scripts

# yarn
yarn add --dev vite-plugin-inject-scripts

# pnpm
pnpm add --save-dev vite-plugin-inject-scripts

# bun
bun add --dev vite-plugin-inject-scripts

Usage

// vite.config.ts
import { defineConfig } from "vite";
import injectScripts from "vite-plugin-inject-scripts";

export default defineConfig({
	plugins: [
		injectScripts({
			scripts: [
				// A script file, injected at the end of <head>.
				{ src: "src/polyfills.js" },
				// An inline module snippet, injected at the start of <body>.
				{ src: "config.js", source: `window.__APP__ = {};`, type: "module", injectTo: "body-prepend", inline: true },
			],
		}),
	],
});

Options

scripts

Type: (string | PriorScript)[]
(Required)

The scripts to inject. Each entry is either a plain path string (equivalent to { src: "…" }) or a PriorScript object. The order in the array is preserved within each injection position.

entryFile

Type: string | true | RegExp | ((jsFilePath: string) => boolean)
Default: true

Selects the entry <script type="module" crossorigin> element in <head>. Scripts injected at head-append are placed immediately before it, so they execute first. If no element matches, head-append scripts are appended to the end of <head>.

  • true (default): the last <script type="module" crossorigin> in <head>.
  • string: the script whose src ends with /name or equals name.
  • RegExp: the first script whose src matches the pattern.
  • function: the first script whose src makes the callback return true.

minifyHtml

Type: boolean | "terser" | "next" | "swc"
Default: true

Minify the HTML output when building. true uses the default engine terser (html-minifier-terser). Pass an engine name to use a different engine — you must install that engine yourself. Set false to disable HTML minification (inline <script> / <style> content is then left untouched as well).

minifyJS

Type: boolean | "oxc" | "swc" | "esbuild" | "terser"
Default: true

Minify the JavaScript output when building. true uses the default engine oxc (oxc-minify). Pass an engine name to use a different engine — you must install that engine yourself. This also controls the minifier used for inline <script> content during HTML minification. Set false to disable.

Note: JSON scripts (.json, importmap, speculationrules, …) are always minified and are not affected by this option.

minifyCSS

Type: boolean | "lightningcss" | "esbuild" | "cssnano" | "clean-css" | "csso"
Default: true

Choose the CSS minification engine used during HTML minification (for inline <style> content). true uses the default engine lightningcss. Pass an engine name to use a different engine — you must install that engine yourself. Set false to disable.

PriorScript

Each script object accepts the following fields:

| Field | Type | Default | Description | | --- | --- | --- | --- | | src | string | — | Path to the script (js / ts / jsx / tsx / json), resolved from Vite's root.When source is set, the file is not read and src is only used for the output file name/extension;When source is set and inline is true, src is only checked for a .json extension to treat the content as JSON. | | source | string | — | Override the script content with an inline string.Not recommended unless the code is very simple — prefer a separate file referenced by src, since inline content loses editor highlighting and autocompletion. | | type | string | "classic" | See script types. | | strict | boolean | false | Prepend a "use strict" directive. Only effective when type is "classic", "script", "iife", "iifearrow", "iife-arrow", or "block". | | injectTo | "head-prepend" \| "head-append" \| "body-prepend" \| "body-append" | "head-append" | Where to insert the <script> tag. | | modify | (code: string) => string | — | Transform the source before it is compiled/minified. | | filterHtml | Filter | — | Only inject into matching HTML files. See filtering. | | inline | boolean | false | Inline the content directly (no src attribute). | | async | boolean | — | The async attribute. | | blocking | ("render")[] | — | The blocking attribute (values are joined). | | crossOrigin | "anonymous" \| "use-credentials" | — | The crossorigin attribute. | | defer | boolean | — | The defer attribute. | | fetchPriority | "high" \| "low" \| "auto" | — | The fetchpriority attribute. | | integrity | string | — | The integrity attribute. | | noModule | boolean | — | The nomodule attribute. | | nonce | boolean | — | The nonce attribute. | | referrerPolicy | string | — | The referrerpolicy attribute. | | (any other) | string | — | Any additional attribute is passed through to the <script> tag. |

type

See HTMLScriptElement.supports() static method.

| Value | Emitted type | Notes | | --- | --- | --- | | "classic" / "script" | (none) | A classic script. Every declared variable and function becomes a global, and import/export cannot be used. | | "module" | type="module" | A JavaScript module. Declared variables and functions are inaccessible from outside, and import/export can be used. With inline: true, export won't error but is meaningless. | | "iife" | (none) | Wraps the content in an IIFE (self-invoking function). Declared variables and functions are inaccessible from outside, and import/export cannot be used. Unlike "module" — which is deferred and non-blocking, so it can run only after the DOM has loaded even when injected before <body> — an IIFE runs synchronously as the parser reaches it. Use "iife" (or "iifearrow" / "block") to run code before the DOM finishes loading (e.g. set a background color early) without leaking variables. | | "iifearrow" / "iife-arrow" | (none) | Same as "iife", but uses an arrow function instead of an anonymous function, so the compiled output is a few bytes smaller. ES6+ only. | | "block" | (none) | Wraps the content in a block scope ({ … }). let, const, class and using declarations do not leak; var does; function declarations leak unless strict is set. A single pair of braces already keeps most declarations from leaking, so it uses fewer bytes than "iife" / "iifearrow" — preferred for inline: true when the code doesn't use var. ES6+ only. | | "importmap" | type="importmap" | An import map (JSON) that controls which URLs are fetched by JavaScript import statements and import() expressions, through its optional imports and scopes keys. | | "speculationrules" | type="speculationrules" | Speculation rules (JSON) that let the browser speculatively prefetch or prerender documents matching URL patterns, to speed up subsequent navigations. | | any other string | type="…" | Passed through verbatim. |

Filtering

filterHtml accepts a string (exact file path match), a RegExp, a function (filename: string) => boolean, or an array of these (matched if any applies). The filter is matched against the resolved path of the HTML file being transformed. Scripts without a filterHtml are injected into every HTML file.

injectScripts({
	scripts: [
		{ src: "src/analytics.js", filterHtml: /index\.html$/ },
		{ src: "src/admin.js", filterHtml: filename => filename.includes("/admin/") },
		{ src: "src/one-off.js", filterHtml: ["/project/index.html", /about\.html$/] },
	],
});

How it works

  • Resolving files: src is resolved relative to Vite's root. Setting source skips the file read.
  • Transformations (in order): modify → IIFE/block wrapping (type: "iife" / "iifearrow" / "iife-arrow" / "block", with strict) → TypeScript compilation (.ts / .tsx / .mts / .cts) → minification (build only).
  • Minification: controlled by minifyHtml, minifyJS and minifyCSS; JSON scripts are always minified.
  • Development: non-inline scripts are served from a /@inject-scripts/… virtual route.
  • Build: non-inline scripts are emitted as assets (.js / .json) and referenced by their hashed file name.

Examples

TypeScript source compiled on the fly

injectScripts({
	scripts: [
		{ src: "src/bootstrap.ts", type: "module" },
	],
});

An IIFE with custom attributes

injectScripts({
	scripts: [
		{ src: "src/legacy.js", type: "iife", injectTo: "body-append", defer: true },
	],
});

Inline a config module before the app

injectScripts({
	scripts: [
		{
			src: "app.config.js",
			source: `export default ${JSON.stringify({ apiBase: "/api" })};`,
			type: "module",
			injectTo: "head-prepend",
			inline: true,
		},
	],
});

Import map

injectScripts({
	scripts: [
		{
			src: "import-map.json",
			source: JSON.stringify({ imports: { vue: "https://esm.sh/vue" } }),
			type: "importmap",
		},
	],
});

Comparison with vite-plugin-inject-script

The two names differ by a single letter, but the plugins do fundamentally different things:

| | vite-plugin-inject-script | vite-plugin-inject-scripts (this plugin) | | --- | --- | --- | | What it modifies | The JavaScript bundle | The HTML document | | Where it injects | Prepends a JavaScript snippet (as a string) to the top of the entry JS file — the one named by Rollup/Rolldown's output.entryFileNames (commonly index.js) | Inserts <script> elements into <head> / <body> | | What it injects | Raw JS source code | <script> tags — inline or external, any type | | Control over placement | Single, fixed position (before all entry code) | Four positions (head-prepend, head-append, body-prepend, body-append) |

In short:

  • vite-plugin-inject-script changes the JavaScript output — it prepends code into the bundled entry file.
  • vite-plugin-inject-scripts changes the HTML output — it adds <script> tags to the page.

License

MIT