Plugins

Configure and extend Packem with Rollup plugins

Plugins

Packem leverages Rollup's powerful plugin ecosystem to extend functionality. You can use any Rollup plugin or create custom plugins to handle specific build requirements.

Basic Plugin Usage

Adding Plugins

import { defineConfig } from '@visulima/packem'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'

export default defineConfig({
  rollup: {
    plugins: [
      nodeResolve(),
      commonjs()
    ]
  }
})

Built-in Plugins

Packem includes several plugins by default:

export default defineConfig({
  // These are enabled automatically
  autoPlugins: {
    nodeResolve: true,
    commonjs: true,
    json: true,
    typescript: true // If using TypeScript
  }
})

Common Plugins

Node Resolution

import { nodeResolve } from '@rollup/plugin-node-resolve'

export default defineConfig({
  rollup: {
    plugins: [
      nodeResolve({
        preferBuiltins: true,
        browser: false,
        exportConditions: ['node']
      })
    ]
  }
})

CommonJS Support

import commonjs from '@rollup/plugin-commonjs'

export default defineConfig({
  rollup: {
    plugins: [
      commonjs({
        include: ['node_modules/**'],
        exclude: ['node_modules/some-esm-package/**']
      })
    ]
  }
})

JSON Import

import json from '@rollup/plugin-json'

export default defineConfig({
  rollup: {
    plugins: [
      json({
        compact: true,
        namedExports: true
      })
    ]
  }
})

Replace Variables

import replace from '@rollup/plugin-replace'

export default defineConfig({
  rollup: {
    plugins: [
      replace({
        'process.env.NODE_ENV': JSON.stringify('production'),
        '__VERSION__': JSON.stringify(process.env.npm_package_version),
        preventAssignment: true
      })
    ]
  }
})

Framework Plugins

React

Quick Setup (Recommended):

Use the add command to automatically configure React:

npx packem add react

This will:

  • Add preset: 'react' to your config
  • Install React, React DOM, and Babel dependencies
  • Automatically detect TypeScript and add @types/react and @types/react-dom if TypeScript is installed
  • Prompt you to add TypeScript if not already installed

Manual Configuration:

Packem provides a built-in React preset that configures Babel for React JSX transformation:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'react', // Automatically configures Babel for React
  transformer,
})

With React Compiler:

import { defineConfig } from '@visulima/packem/config'
import { createReactPreset } from '@visulima/packem/config/preset/react'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createReactPreset({
    compiler: true // Enable React Compiler optimization
  }),
  transformer,
})

Manual Babel Configuration:

If you need more control, you can configure Babel manually:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  transformer,
  externals: ['react', 'react-dom'],
  rollup: {
    babel: {
      presets: [
        ['@babel/preset-react', { runtime: 'automatic' }]
        // Note: TypeScript is handled by the transformer via parser plugins
        // Babel only needs to parse TypeScript syntax, not transform it
      ],
      plugins: [
        // Add custom Babel plugins here
      ]
    }
  }
})

Note: When using Babel with React, Babel runs before your main transformer (esbuild/SWC/etc.), transforming JSX into JavaScript. TypeScript is handled by the transformer via parser plugins, not by Babel. This ensures that Rollup plugins can properly parse the code.

SolidJS

Quick Setup (Recommended):

Use the add command to automatically configure SolidJS:

npx packem add solid

This will:

  • Add preset: 'solid' to your config
  • Install SolidJS and Babel dependencies

Manual Configuration:

Packem provides a built-in Solid preset that configures Babel for SolidJS JSX transformation:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'solid', // Automatically configures Babel for SolidJS
  transformer,
})

With SSR Support:

import { defineConfig } from '@visulima/packem/config'
import { createSolidPreset } from '@visulima/packem/config/preset/solid'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createSolidPreset({
    solidOptions: {
      generate: 'ssr',
      hydratable: true
    }
  }),
  transformer,
})

Manual Babel Configuration:

If you need more control, you can configure Babel manually:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  transformer,
  externals: ['solid-js', 'solid-js/web', 'solid-js/store'],
  rollup: {
    babel: {
      presets: [
        ['babel-preset-solid', { generate: 'dom' }],
        ['@babel/preset-env', { bugfixes: true, targets: 'last 2 years' }]
        // Note: TypeScript is handled by the transformer via parser plugins
        // Babel only needs to parse TypeScript syntax, not transform it
      ],
      plugins: [
        // Add custom Babel plugins here
      ]
    }
  }
})

Note: When using Babel with SolidJS, Babel runs before your main transformer (esbuild/SWC/etc.), transforming JSX into JavaScript. TypeScript is handled by the transformer via parser plugins, not by Babel. This ensures that Rollup plugins can properly parse the code.

Vue

import Vue from 'unplugin-vue/rollup'

export default defineConfig({
  rollup: {
    plugins: [
      Vue({
        include: [/\.vue$/],
        customElement: false
      })
    ]
  }
})

Note: For Vue 3 projects, you can also use the Vue preset instead of manually configuring the plugin:

export default defineConfig({
  preset: 'vue'
})

Svelte

import svelte from 'rollup-plugin-svelte'

export default defineConfig({
  rollup: {
    plugins: [
      svelte({
        compilerOptions: {
          hydratable: true
        }
      })
    ]
  }
})

Note: For Svelte projects, you can also use the Svelte preset instead of manually configuring the plugin:

export default defineConfig({
  preset: 'svelte'
})

Asset Plugins

Copy Files

import copy from 'rollup-plugin-copy'

export default defineConfig({
  rollup: {
    plugins: [
      copy({
        targets: [
          { src: 'src/assets/*', dest: 'dist/assets' },
          { src: 'public/favicon.ico', dest: 'dist' }
        ]
      })
    ]
  }
})

Image Processing

import image from '@rollup/plugin-image'

export default defineConfig({
  rollup: {
    plugins: [
      image({
        include: ['**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif'],
        limit: 8192 // Inline images smaller than 8KB
      })
    ]
  }
})

URL Assets

import url from '@rollup/plugin-url'

export default defineConfig({
  rollup: {
    plugins: [
      url({
        limit: 10 * 1024, // 10KB
        include: ['**/*.svg', '**/*.png', '**/*.jpg'],
        emitFiles: true
      })
    ]
  }
})

Data URI imports (?data-uri)

Inline files as data URIs at import sites. SVG files are encoded as tiny, human-readable data URIs; other file types are base64-encoded.

import { defineConfig } from '@visulima/packem/config'
import { dataUriPlugin } from '@visulima/packem-rollup/plugins'

export default defineConfig({
  rollup: {
    plugins: [
      dataUriPlugin({
        // Optional: when using output in an <img srcset="..."> attribute
        // spaces are encoded as %20 to be srcset-safe
        srcset: false
      })
    ]
  }
})

Usage examples:

// SVG ? tiny data URI (not base64), whitespace-collapsed
import icon from './assets/icon.svg?data-uri'

// Any other file ? base64 data URI with appropriate mime type
import txt from './assets/readme.txt?data-uri'

Notes:

  • Only imports ending with ?data-uri are transformed; regular imports are unaffected.
  • SVGs are encoded as data:image/svg+xml,.... (tiny URI). Others as data:<mime>;base64,....
  • For srcset attributes, enable { srcset: true } to encode spaces as %20.

Development Plugins

Live Reload

import livereload from 'rollup-plugin-livereload'

export default defineConfig({
  rollup: {
    plugins: [
      ...(process.env.NODE_ENV === 'development' ? [
        livereload({
          watch: 'dist',
          port: 35729
        })
      ] : [])
    ]
  }
})

Serve Files

import serve from 'rollup-plugin-serve'

export default defineConfig({
  rollup: {
    plugins: [
      ...(process.env.NODE_ENV === 'development' ? [
        serve({
          open: true,
          contentBase: 'dist',
          port: 3000
        })
      ] : [])
    ]
  }
})

Analysis Plugins

Bundle Analyzer

import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  rollup: {
    plugins: [
      visualizer({
        filename: 'dist/stats.html',
        open: true,
        gzipSize: true
      })
    ]
  }
})

Bundle Size

import bundleSize from 'rollup-plugin-bundle-size'

export default defineConfig({
  rollup: {
    plugins: [
      bundleSize()
    ]
  }
})

License Check

import license from 'rollup-plugin-license'

export default defineConfig({
  rollup: {
    plugins: [
      license({
        sourcemap: true,
        banner: {
          commentStyle: 'regular',
          content: {
            file: path.join(__dirname, 'LICENSE')
          }
        },
        thirdParty: {
          output: path.join(__dirname, 'dist', 'dependencies.txt')
        }
      })
    ]
  }
})

Custom Plugins

Simple Plugin

function customPlugin() {
  return {
    name: 'custom-plugin',

    buildStart() {
      console.log('Build started!')
    },

    transform(code, id) {
      if (id.endsWith('.custom')) {
        return `export default ${JSON.stringify(code)}`
      }
      return null
    },

    generateBundle() {
      console.log('Bundle generated!')
    }
  }
}

export default defineConfig({
  rollup: {
    plugins: [
      customPlugin()
    ]
  }
})

Advanced Plugin

function advancedPlugin(options = {}) {
  const { include = '**/*.js', exclude = 'node_modules/**' } = options

  return {
    name: 'advanced-plugin',

    buildStart(opts) {
      // Plugin initialization
      this.addWatchFile('config.json')
    },

    resolveId(id, importer) {
      // Custom module resolution
      if (id === 'virtual:my-module') {
        return id
      }
      return null
    },

    load(id) {
      // Load custom modules
      if (id === 'virtual:my-module') {
        return 'export const msg = "Hello from virtual module"'
      }
      return null
    },

    transform(code, id) {
      // Transform code
      if (include.test && include.test(id) && !exclude.test(id)) {
        return {
          code: code.replace(/OLD_SYNTAX/g, 'NEW_SYNTAX'),
          map: null
        }
      }
      return null
    }
  }
}

Plugin Configuration

Conditional Plugins

export default defineConfig({
  rollup: {
    plugins: [
      // Always included
      nodeResolve(),
      commonjs(),

      // Development only
      ...(process.env.NODE_ENV === 'development' ? [
        serve({ contentBase: 'dist' }),
        livereload()
      ] : []),

      // Production only
      ...(process.env.NODE_ENV === 'production' ? [
        terser(),
        visualizer()
      ] : [])
    ]
  }
})

Plugin Options

export default defineConfig({
  rollup: {
    plugins: [
      nodeResolve({
        // Prefer built-in Node.js modules
        preferBuiltins: true,

        // Include/exclude packages
        include: ['node_modules/**'],
        exclude: ['node_modules/problematic-package/**'],

        // Custom conditions
        exportConditions: ['node', 'import'],

        // Browser compatibility
        browser: false
      })
    ]
  }
})

Plugin Ordering

export default defineConfig({
  rollup: {
    plugins: [
      // 1. Module resolution
      nodeResolve(),

      // 2. Format conversion
      commonjs(),

      // 3. Code transformation
      typescript(),
      // Babel is configured via rollup.babel option or React preset
      // See React section above for examples

      // 4. Asset processing
      postcss(),
      image(),

      // 5. Development tools
      ...(isDev ? [serve(), livereload()] : []),

      // 6. Production optimization
      ...(isProd ? [terser(), visualizer()] : [])
    ]
  }
})

Plugin Ecosystem

Essential Plugins

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import replace from '@rollup/plugin-replace'
import json from '@rollup/plugin-json'

export default defineConfig({
  transformer,
  rollup: {
    // Babel is configured via rollup.babel option (not as a plugin)
    // For React, use preset: 'react' instead
    babel: {
      presets: ['@babel/preset-env'],
      plugins: []
    },
    plugins: [
      nodeResolve(),
      commonjs(),
      replace({ preventAssignment: true }),
      json()
    ]
  }
})

CSS Plugins

import postcss from 'rollup-plugin-postcss'
import autoprefixer from 'autoprefixer'
import cssnano from 'cssnano'

export default defineConfig({
  rollup: {
    plugins: [
      postcss({
        extract: true,
        minimize: true,
        plugins: [
          autoprefixer(),
          cssnano()
        ]
      })
    ]
  }
})

Testing Plugins

import istanbul from 'rollup-plugin-istanbul'

export default defineConfig({
  rollup: {
    plugins: [
      ...(process.env.NODE_ENV === 'test' ? [
        istanbul({
          exclude: ['test/**/*', 'node_modules/**/*']
        })
      ] : [])
    ]
  }
})

Plugin Development

Plugin Template

interface PluginOptions {
  include?: string | RegExp | (string | RegExp)[]
  exclude?: string | RegExp | (string | RegExp)[]
  [key: string]: any
}

function myPlugin(options: PluginOptions = {}): Plugin {
  const filter = createFilter(options.include, options.exclude)

  return {
    name: 'my-plugin',

    buildStart(opts) {
      // Initialize plugin
    },

    resolveId(id, importer) {
      // Resolve imports
      return null
    },

    load(id) {
      // Load modules
      return null
    },

    transform(code, id) {
      if (!filter(id)) return null

      // Transform code
      return {
        code: transformedCode,
        map: sourceMap
      }
    },

    generateBundle(opts, bundle) {
      // Process bundle
    },

    writeBundle(opts, bundle) {
      // After bundle written
    }
  }
}

Plugin Testing

import { rollup } from 'rollup'
import myPlugin from './my-plugin'

async function testPlugin() {
  const bundle = await rollup({
    input: 'test/fixtures/input.js',
    plugins: [
      myPlugin({
        // Test options
      })
    ]
  })

  const { output } = await bundle.generate({
    format: 'esm'
  })

  console.log(output[0].code)
}

Troubleshooting

Plugin Conflicts

Some plugins may conflict with each other. Check plugin documentation for compatibility.

export default defineConfig({
  rollup: {
    plugins: [
      // Order matters - resolve conflicts by reordering
      nodeResolve(),
      commonjs(),

      // Avoid duplicate functionality
      // Note: When using Babel with React preset, Babel handles JSX transformation
      // TypeScript is handled by the transformer (esbuild/SWC) via parser plugins
    ]
  }
})

Plugin Errors

export default defineConfig({
  rollup: {
    onwarn(warning, warn) {
      // Handle plugin warnings
      if (warning.plugin === 'problematic-plugin') {
        return // Ignore warnings from this plugin
      }
      warn(warning)
    }
  }
})

Performance Issues

export default defineConfig({
  rollup: {
    plugins: [
      // Use plugin caching
      somePlugin({
        cache: true
      }),

      // Limit plugin scope
      anotherPlugin({
        include: 'src/**',
        exclude: 'node_modules/**'
      })
    ]
  }
})

  • Rollup - Rollup configuration
  • Transform - Code transformation
  • Assets - Asset handling
  • External - External dependencies
Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues