Files
next.orly.dev/.claude/skills/rollup/SKILL.md
mleku 8ea91e39d8 Add Claude Code skills for web frontend frameworks
- Add Svelte 3/4 skill covering components, reactivity, stores, lifecycle
- Add Rollup skill covering configuration, plugins, code splitting
- Add nostr-tools skill covering event creation, signing, relay communication
- Add applesauce-core skill covering event stores, reactive queries
- Add applesauce-signers skill covering NIP-07/NIP-46 signing abstractions
- Update .gitignore to include .claude/** directory

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 06:56:57 +00:00

900 lines
17 KiB
Markdown

---
name: rollup
description: This skill should be used when working with Rollup module bundler, including configuration, plugins, code splitting, and build optimization. Provides comprehensive knowledge of Rollup patterns, plugin development, and bundling strategies.
---
# Rollup Skill
This skill provides comprehensive knowledge and patterns for working with Rollup module bundler effectively.
## When to Use This Skill
Use this skill when:
- Configuring Rollup for web applications
- Setting up Rollup for library builds
- Working with Rollup plugins
- Implementing code splitting
- Optimizing bundle size
- Troubleshooting build issues
- Integrating Rollup with Svelte or other frameworks
- Developing custom Rollup plugins
## Core Concepts
### Rollup Overview
Rollup is a module bundler that:
- **Tree-shakes by default** - Removes unused code automatically
- **ES module focused** - Native ESM output support
- **Plugin-based** - Extensible architecture
- **Multiple outputs** - Generate multiple formats from single input
- **Code splitting** - Dynamic imports for lazy loading
- **Scope hoisting** - Flattens modules for smaller bundles
### Basic Configuration
```javascript
// rollup.config.js
export default {
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
}
};
```
### Output Formats
Rollup supports multiple output formats:
| Format | Description | Use Case |
|--------|-------------|----------|
| `esm` | ES modules | Modern browsers, bundlers |
| `cjs` | CommonJS | Node.js |
| `iife` | Self-executing function | Script tags |
| `umd` | Universal Module Definition | CDN, both environments |
| `amd` | Asynchronous Module Definition | RequireJS |
| `system` | SystemJS | SystemJS loader |
## Configuration
### Full Configuration Options
```javascript
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
// Entry point(s)
input: 'src/main.js',
// Output configuration
output: {
// Output file or directory
file: 'dist/bundle.js',
// Or for code splitting:
// dir: 'dist',
// Output format
format: 'esm',
// Name for IIFE/UMD builds
name: 'MyBundle',
// Sourcemap generation
sourcemap: true,
// Global variables for external imports (IIFE/UMD)
globals: {
jquery: '$'
},
// Banner/footer comments
banner: '/* My library v1.0.0 */',
footer: '/* End of bundle */',
// Chunk naming for code splitting
chunkFileNames: '[name]-[hash].js',
entryFileNames: '[name].js',
// Manual chunks for code splitting
manualChunks: {
vendor: ['lodash', 'moment']
},
// Interop mode for default exports
interop: 'auto',
// Preserve modules structure
preserveModules: false,
// Exports mode
exports: 'auto' // 'default', 'named', 'none', 'auto'
},
// External dependencies (not bundled)
external: ['lodash', /^node:/],
// Plugin array
plugins: [
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
production && terser()
],
// Watch mode options
watch: {
include: 'src/**',
exclude: 'node_modules/**',
clearScreen: false
},
// Warning handling
onwarn(warning, warn) {
// Skip certain warnings
if (warning.code === 'CIRCULAR_DEPENDENCY') return;
warn(warning);
},
// Preserve entry signatures for code splitting
preserveEntrySignatures: 'strict',
// Treeshake options
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false
}
};
```
### Multiple Outputs
```javascript
export default {
input: 'src/main.js',
output: [
{
file: 'dist/bundle.esm.js',
format: 'esm'
},
{
file: 'dist/bundle.cjs.js',
format: 'cjs'
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'MyLibrary'
}
]
};
```
### Multiple Entry Points
```javascript
export default {
input: {
main: 'src/main.js',
utils: 'src/utils.js'
},
output: {
dir: 'dist',
format: 'esm'
}
};
```
### Array of Configurations
```javascript
export default [
{
input: 'src/main.js',
output: { file: 'dist/main.js', format: 'esm' }
},
{
input: 'src/worker.js',
output: { file: 'dist/worker.js', format: 'iife' }
}
];
```
## Essential Plugins
### @rollup/plugin-node-resolve
Resolve node_modules imports:
```javascript
import resolve from '@rollup/plugin-node-resolve';
export default {
plugins: [
resolve({
// Resolve browser field in package.json
browser: true,
// Prefer built-in modules
preferBuiltins: true,
// Only resolve these extensions
extensions: ['.mjs', '.js', '.json', '.node'],
// Dedupe packages (important for Svelte)
dedupe: ['svelte'],
// Main fields to check in package.json
mainFields: ['module', 'main', 'browser'],
// Export conditions
exportConditions: ['svelte', 'browser', 'module', 'import']
})
]
};
```
### @rollup/plugin-commonjs
Convert CommonJS to ES modules:
```javascript
import commonjs from '@rollup/plugin-commonjs';
export default {
plugins: [
commonjs({
// Include specific modules
include: /node_modules/,
// Exclude specific modules
exclude: ['node_modules/lodash-es/**'],
// Ignore conditional requires
ignoreDynamicRequires: false,
// Transform mixed ES/CJS modules
transformMixedEsModules: true,
// Named exports for specific modules
namedExports: {
'react': ['createElement', 'Component']
}
})
]
};
```
### @rollup/plugin-terser
Minify output:
```javascript
import terser from '@rollup/plugin-terser';
export default {
plugins: [
terser({
compress: {
drop_console: true,
drop_debugger: true
},
mangle: true,
format: {
comments: false
}
})
]
};
```
### rollup-plugin-svelte
Compile Svelte components:
```javascript
import svelte from 'rollup-plugin-svelte';
import css from 'rollup-plugin-css-only';
export default {
plugins: [
svelte({
// Enable dev mode
dev: !production,
// Emit CSS as a separate file
emitCss: true,
// Preprocess (SCSS, TypeScript, etc.)
preprocess: sveltePreprocess(),
// Compiler options
compilerOptions: {
dev: !production
},
// Custom element mode
customElement: false
}),
// Extract CSS to separate file
css({ output: 'bundle.css' })
]
};
```
### Other Common Plugins
```javascript
import json from '@rollup/plugin-json';
import replace from '@rollup/plugin-replace';
import alias from '@rollup/plugin-alias';
import image from '@rollup/plugin-image';
import copy from 'rollup-plugin-copy';
import livereload from 'rollup-plugin-livereload';
export default {
plugins: [
// Import JSON files
json(),
// Replace strings in code
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify('production'),
'__VERSION__': JSON.stringify('1.0.0')
}),
// Path aliases
alias({
entries: [
{ find: '@', replacement: './src' },
{ find: 'utils', replacement: './src/utils' }
]
}),
// Import images
image(),
// Copy static files
copy({
targets: [
{ src: 'public/*', dest: 'dist' }
]
}),
// Live reload in dev
!production && livereload('dist')
]
};
```
## Code Splitting
### Dynamic Imports
```javascript
// Automatically creates chunks
async function loadFeature() {
const { feature } = await import('./feature.js');
feature();
}
```
Configuration for code splitting:
```javascript
export default {
input: 'src/main.js',
output: {
dir: 'dist',
format: 'esm',
chunkFileNames: 'chunks/[name]-[hash].js'
}
};
```
### Manual Chunks
```javascript
export default {
output: {
manualChunks: {
// Vendor chunk
vendor: ['lodash', 'moment'],
// Or use a function for more control
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}
}
}
};
```
### Advanced Chunking Strategy
```javascript
export default {
output: {
manualChunks(id, { getModuleInfo }) {
// Separate chunks by feature
if (id.includes('/features/auth/')) {
return 'auth';
}
if (id.includes('/features/dashboard/')) {
return 'dashboard';
}
// Vendor chunks by package
if (id.includes('node_modules')) {
const match = id.match(/node_modules\/([^/]+)/);
if (match) {
const packageName = match[1];
// Group small packages
const smallPackages = ['lodash', 'date-fns'];
if (smallPackages.includes(packageName)) {
return 'vendor-utils';
}
return `vendor-${packageName}`;
}
}
}
}
};
```
## Watch Mode
### Configuration
```javascript
export default {
watch: {
// Files to watch
include: 'src/**',
// Files to ignore
exclude: 'node_modules/**',
// Don't clear screen on rebuild
clearScreen: false,
// Rebuild delay
buildDelay: 0,
// Watch chokidar options
chokidar: {
usePolling: true
}
}
};
```
### CLI Watch Mode
```bash
# Watch mode
rollup -c -w
# With environment variable
ROLLUP_WATCH=true rollup -c
```
## Plugin Development
### Plugin Structure
```javascript
function myPlugin(options = {}) {
return {
// Plugin name (required)
name: 'my-plugin',
// Build hooks
options(inputOptions) {
// Modify input options
return inputOptions;
},
buildStart(inputOptions) {
// Called on build start
},
resolveId(source, importer, options) {
// Custom module resolution
if (source === 'virtual-module') {
return source;
}
return null; // Defer to other plugins
},
load(id) {
// Load module content
if (id === 'virtual-module') {
return 'export default "Hello"';
}
return null;
},
transform(code, id) {
// Transform module code
if (id.endsWith('.txt')) {
return {
code: `export default ${JSON.stringify(code)}`,
map: null
};
}
},
buildEnd(error) {
// Called when build ends
if (error) {
console.error('Build failed:', error);
}
},
// Output generation hooks
renderStart(outputOptions, inputOptions) {
// Called before output generation
},
banner() {
return '/* Custom banner */';
},
footer() {
return '/* Custom footer */';
},
renderChunk(code, chunk, options) {
// Transform output chunk
return code;
},
generateBundle(options, bundle) {
// Modify output bundle
for (const fileName in bundle) {
const chunk = bundle[fileName];
if (chunk.type === 'chunk') {
// Modify chunk
}
}
},
writeBundle(options, bundle) {
// After bundle is written
},
closeBundle() {
// Called when bundle is closed
}
};
}
export default myPlugin;
```
### Plugin with Rollup Utils
```javascript
import { createFilter } from '@rollup/pluginutils';
function myTransformPlugin(options = {}) {
const filter = createFilter(options.include, options.exclude);
return {
name: 'my-transform',
transform(code, id) {
if (!filter(id)) return null;
// Transform code
const transformed = code.replace(/foo/g, 'bar');
return {
code: transformed,
map: null // Or generate sourcemap
};
}
};
}
```
## Svelte Integration
### Complete Svelte Setup
```javascript
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import css from 'rollup-plugin-css-only';
import livereload from 'rollup-plugin-livereload';
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require('child_process').spawn(
'npm',
['run', 'start', '--', '--dev'],
{
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
}
);
process.on('SIGTERM', toExit);
process.on('exit', toExit);
}
};
}
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
compilerOptions: {
dev: !production
}
}),
css({ output: 'bundle.css' }),
resolve({
browser: true,
dedupe: ['svelte']
}),
commonjs(),
// Dev server
!production && serve(),
!production && livereload('public'),
// Minify in production
production && terser()
],
watch: {
clearScreen: false
}
};
```
## Best Practices
### Bundle Optimization
1. **Enable tree shaking** - Use ES modules
2. **Mark side effects** - Set `sideEffects` in package.json
3. **Use terser** - Minify production builds
4. **Analyze bundles** - Use rollup-plugin-visualizer
5. **Code split** - Lazy load routes and features
### External Dependencies
```javascript
export default {
// Don't bundle peer dependencies for libraries
external: [
'react',
'react-dom',
/^lodash\//
],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
};
```
### Development vs Production
```javascript
const production = !process.env.ROLLUP_WATCH;
export default {
plugins: [
replace({
preventAssignment: true,
'process.env.NODE_ENV': JSON.stringify(
production ? 'production' : 'development'
)
}),
production && terser()
].filter(Boolean)
};
```
### Error Handling
```javascript
export default {
onwarn(warning, warn) {
// Ignore circular dependency warnings
if (warning.code === 'CIRCULAR_DEPENDENCY') {
return;
}
// Ignore unused external imports
if (warning.code === 'UNUSED_EXTERNAL_IMPORT') {
return;
}
// Treat other warnings as errors
if (warning.code === 'UNRESOLVED_IMPORT') {
throw new Error(warning.message);
}
// Use default warning handling
warn(warning);
}
};
```
## Common Patterns
### Library Build
```javascript
import pkg from './package.json';
export default {
input: 'src/index.js',
external: Object.keys(pkg.peerDependencies || {}),
output: [
{
file: pkg.main,
format: 'cjs',
sourcemap: true
},
{
file: pkg.module,
format: 'esm',
sourcemap: true
}
]
};
```
### Application Build
```javascript
export default {
input: 'src/main.js',
output: {
dir: 'dist',
format: 'esm',
chunkFileNames: 'chunks/[name]-[hash].js',
entryFileNames: '[name]-[hash].js',
sourcemap: true
},
plugins: [
// All dependencies bundled
resolve({ browser: true }),
commonjs(),
terser()
]
};
```
### Web Worker Build
```javascript
export default [
// Main application
{
input: 'src/main.js',
output: {
file: 'dist/main.js',
format: 'esm'
},
plugins: [resolve(), commonjs()]
},
// Web worker (IIFE format)
{
input: 'src/worker.js',
output: {
file: 'dist/worker.js',
format: 'iife'
},
plugins: [resolve(), commonjs()]
}
];
```
## Troubleshooting
### Common Issues
**Module not found:**
- Check @rollup/plugin-node-resolve is configured
- Verify package is installed
- Check `external` array
**CommonJS module issues:**
- Add @rollup/plugin-commonjs
- Check `namedExports` configuration
- Try `transformMixedEsModules: true`
**Circular dependencies:**
- Use `onwarn` to suppress or fix
- Refactor to break cycles
- Check import order
**Sourcemaps not working:**
- Set `sourcemap: true` in output
- Ensure plugins pass through maps
- Check browser devtools settings
**Large bundle size:**
- Use rollup-plugin-visualizer
- Check for duplicate dependencies
- Verify tree shaking is working
- Mark unused packages as external
## CLI Reference
```bash
# Basic build
rollup -c
# Watch mode
rollup -c -w
# Custom config
rollup -c rollup.custom.config.js
# Output format
rollup src/main.js --format esm --file dist/bundle.js
# Environment variables
NODE_ENV=production rollup -c
# Silent mode
rollup -c --silent
# Generate bundle stats
rollup -c --perf
```
## References
- **Rollup Documentation**: https://rollupjs.org
- **Plugin Directory**: https://github.com/rollup/plugins
- **Awesome Rollup**: https://github.com/rollup/awesome
- **GitHub**: https://github.com/rollup/rollup
## Related Skills
- **svelte** - Using Rollup with Svelte
- **typescript** - TypeScript compilation with Rollup
- **nostr-tools** - Bundling Nostr applications