Skip to content

A zero-dependency Vite plugin and standalone utility to inline CSS, JavaScript, and SVG assets into HTML for single-file deployment.

License

Notifications You must be signed in to change notification settings

ropean/inline-assets

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

60 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@ropean/inline-assets

Deploy Website Live Site license npm version downloads

A zero-dependency Vite plugin and standalone utility to inline CSS, JavaScript, and SVG assets into HTML for single-file deployment.

โœจ Features

  • ๐Ÿš€ Zero Dependencies - No external packages required
  • ๐Ÿ”Œ Dual Mode - Use as Vite plugin or standalone function
  • ๐ŸŽจ Inline CSS - Converts <link> tags to <style> tags
  • ๐Ÿ“ฆ Inline JavaScript - Converts <script src> to inline <script>
  • ๐Ÿ–ผ๏ธ Inline SVG - Converts SVG files to base64 data URIs
  • ๐ŸŽฏ Selective Inlining - Exclude specific files with patterns
  • ๐Ÿงน Auto Cleanup - Removes inlined files and empty directories
  • ๐Ÿ“ Custom Logger - Bring your own logger or use the built-in one
  • ๐Ÿ”ง TypeScript Support - Full type definitions included

๐Ÿ“ฆ Installation

npm install @ropean/inline-assets -D
yarn add @ropean/inline-assets -D
pnpm add @ropean/inline-assets -D

๐Ÿš€ Usage

As a Vite Plugin

// vite.config.ts
import { defineConfig } from 'vite';
import inlineAssets from '@ropean/inline-assets';

export default defineConfig({
  plugins: [
    inlineAssets({
      css: true,
      js: true,
      svg: { img: false, link: true },
      excludes: ['assets/large-file.js'],
    }),
  ],
});

As a Standalone Function

Perfect for use with any build tool (Webpack, Rollup, esbuild, etc.):

import { inlineAssets } from '@ropean/inline-assets';

// After your build process
await inlineAssets({
  htmlPath: './dist/index.html',
  css: true,
  js: true,
  svg: { img: true, link: true },
});

With Custom Logger

import { inlineAssets } from '@ropean/inline-assets';

const myLogger = {
  info: (msg) => console.log('[INFO]', msg),
  success: (msg) => console.log('[โœ“]', msg),
  warning: (msg) => console.warn('[โš ]', msg),
  error: (msg) => console.error('[โœ–]', msg),
};

await inlineAssets({
  htmlPath: './dist/index.html',
  logger: myLogger,
});

// Or disable logging completely
await inlineAssets({
  htmlPath: './dist/index.html',
  logger: false,
});

โš™๏ธ Options

Vite Plugin Options

interface VitePluginOptions {
  /** Whether to inline CSS files (default: true) */
  css?: boolean;

  /** Whether to inline JavaScript files (default: true) */
  js?: boolean;

  /** SVG inlining options (default: { img: false, link: true }) */
  svg?:
    | boolean
    | {
        img?: boolean; // Inline SVG in <img> tags
        link?: boolean; // Inline SVG in <link> tags (favicon)
      };

  /** File patterns to exclude from inlining (default: []) */
  excludes?: string[];

  /** Distribution directory name (default: 'dist') */
  distDir?: string;

  /** HTML file name to process (default: 'index.html') */
  htmlFileName?: string;

  /** Where to insert inlined CSS (default: 'original') */
  cssInsertPosition?: 'original' | 'head-start' | 'head-end';

  /** Custom logger or false to disable (default: built-in logger) */
  logger?: LoggerInterface | false;
}

Standalone Function Options

interface InlineAssetsOptions {
  /** Path to the HTML file to process (required) */
  htmlPath: string;

  /** Base directory for resolving asset paths (default: HTML file's directory) */
  baseDir?: string;

  /** Whether to inline CSS files (default: true) */
  css?: boolean;

  /** Whether to inline JavaScript files (default: true) */
  js?: boolean;

  /** SVG inlining options (default: true) */
  svg?:
    | boolean
    | {
        img?: boolean;
        link?: boolean;
      };

  /** File patterns to exclude from inlining (default: []) */
  excludes?: string[];

  /** Whether to delete inlined asset files (default: true) */
  removeInlinedFiles?: boolean;

  /** Whether to remove empty directories (default: true) */
  cleanupEmptyDirs?: boolean;

  /** Where to insert inlined CSS (default: 'original') */
  cssInsertPosition?: 'original' | 'head-start' | 'head-end';

  /** Custom logger or false to disable (default: built-in logger) */
  logger?: LoggerInterface | false;
}

๐Ÿ“ Custom Logger Interface

Implement this interface to create your own logger:

interface LoggerInterface {
  info(message: string): void;
  success(message: string): void;
  warning(message: string): void;
  error(message: string): void;
  event?(message: string): void; // Optional
  file?(path: string): string; // Optional
  newline?(count?: number): void; // Optional
}

๐ŸŽฏ CSS Insert Position

Control where inlined CSS is placed in your HTML:

inlineAssets({
  cssInsertPosition: 'original', // Default: keep CSS at original <link> position
});

Available Options

  • 'original' (default) - Keeps CSS at the original <link> tag position

    • โœ… Preserves the order of CSS and JS
    • โœ… CSS appears before JS if that's how you structured it
    • โš ๏ธ May create multiple <style> tags if you have multiple CSS files
  • 'head-start' - Moves all CSS to the beginning of <head>

    • โœ… Optimal for performance (CSS loads first)
    • โœ… Single merged <style> tag
    • โš ๏ธ Changes the original order
  • 'head-end' - Moves all CSS to the end of <head>

    • โœ… Single merged <style> tag
    • โš ๏ธ CSS loads after other head elements

Example

// Keep CSS before JS (preserves order)
inlineAssets({
  cssInsertPosition: 'original',
});

// Optimize for performance (CSS at top)
inlineAssets({
  cssInsertPosition: 'head-start',
});

๐ŸŽฏ Exclusion Patterns

Exclude specific files from inlining:

inlineAssets({
  excludes: [
    'index.js', // Matches any file named 'index.js'
    'assets/vendor.js', // Matches 'assets/vendor.js' specifically
    'large-image.svg', // Matches any file named 'large-image.svg'
  ],
});

๐Ÿ”ง How It Works

Vite Plugin Mode

  1. Config Hook: Automatically configures Vite to extract CSS as a single file
  2. Build: Vite builds your project normally
  3. Close Bundle Hook: After build completes, inlines assets into HTML
  4. Cleanup: Removes inlined files and empty directories

Standalone Mode

  1. Reads the HTML file
  2. Finds all CSS, JS, and SVG references
  3. Inlines their content (CSS/JS as text, SVG as base64)
  4. Writes the modified HTML back
  5. Optionally removes inlined files

๐Ÿ“Š Example Output

Before (3 files):

dist/
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ assets/
โ”‚   โ”œโ”€โ”€ index.css
โ”‚   โ””โ”€โ”€ index.js

After (1 file):

dist/
โ””โ”€โ”€ index.html  (with inlined CSS and JS)

๐Ÿค Integration Examples

See the examples/ directory for complete, runnable examples:

Quick Start Examples

Webpack Integration
// webpack.config.js
const { inlineAssets } = require('@ropean/inline-assets');

module.exports = {
  plugins: [
    {
      apply: (compiler) => {
        compiler.hooks.done.tap('InlineAssets', async () => {
          await inlineAssets({ htmlPath: './dist/index.html' });
        });
      },
    },
  ],
};

๐Ÿ‘‰ View full example

Rollup Integration
// rollup.config.js
import { inlineAssets } from '@ropean/inline-assets';

export default {
  plugins: [
    {
      name: 'inline-assets',
      closeBundle: async () => {
        await inlineAssets({ htmlPath: './dist/index.html' });
      },
    },
  ],
};

๐Ÿ‘‰ View full example

npm Scripts
{
  "scripts": {
    "build": "vite build",
    "postbuild": "node inline-assets.js"
  }
}
// inline-assets.js
import { inlineAssets } from '@ropean/inline-assets';

const result = await inlineAssets({
  htmlPath: './dist/index.html',
});

if (!result.success) {
  console.error('Failed to inline assets');
  process.exit(1);
}

๐Ÿ‘‰ View full example with error handling

Gulp Integration
// gulpfile.js
import { inlineAssets } from '@ropean/inline-assets';
import gulp from 'gulp';

gulp.task('inline', async () => {
  await inlineAssets({ htmlPath: './dist/index.html' });
});

gulp.task('build', gulp.series('your-build-task', 'inline'));

๐Ÿ‘‰ View full example with error handling

๐Ÿ› Troubleshooting

CSS not inlining properly?

Make sure you're not using cssCodeSplit: true in your Vite config. The plugin automatically sets this to false.

Assets not found?

Check that baseDir points to the correct directory where your assets are located.

Want to keep some files external?

Use the excludes option to prevent specific files from being inlined.

๐Ÿ“„ License

MIT ยฉ ropean

๐Ÿ™ Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

๐Ÿ“ฎ Issues

Found a bug or have a feature request? Open an issue

About

A zero-dependency Vite plugin and standalone utility to inline CSS, JavaScript, and SVG assets into HTML for single-file deployment.

Topics

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •