# Webpack Configuration CLAUDE.md

## Overview

The Storyteller SDK uses webpack 5 with multiple configuration files for different build targets. The build system prioritizes bundle size optimization (using Preact instead of React), supports both web distribution and NPM packaging, and includes development features like Module Federation and hot module replacement. All webpack configs use CommonJS format (`.cjs` extension) for Node.js compatibility.

## Architecture

### Configuration Files

```
/
├── webpack.common.cjs         # Shared base configuration
├── webpack.dev.cjs            # Development build
├── webpack.prod.cjs           # Production web build
├── webpack.npm.cjs            # NPM package build
├── postcss.config.cjs         # PostCSS/Autoprefixer
├── tsconfig.json              # TypeScript configuration
└── index.cjs                  # CommonJS entry wrapper
```

### Build Targets

- **Development**: Fast builds with HMR and source maps
- **Production Web**: Minified bundles for CDN distribution
- **NPM Package**: CommonJS module for NPM distribution

### Entry Points

```javascript
// Web builds
{
  'storyteller': './src/index.ts',           // Main SDK
  'storyteller.page': './src/storyPage.index.ts'  // Page-specific
}

// Production adds .min suffix
{
  'storyteller.min': './src/index.ts',
  'storyteller.page.min': './src/storyPage.index.ts'
}
```

## Common Tasks

### Running Different Builds

```bash
# Development with watch mode
npm run watch                    # SDK only
npm run start                    # SDK + demo site + proxy

# Production builds
npm run build                    # Web distribution
npm run build:npm                # NPM package
npm run build:prod:sdk-and-demo  # Everything

# Analysis
npm run analyze:npm               # Bundle size analysis
```

### Adding a New Entry Point

```javascript
// In webpack.dev.cjs/webpack.npm.cjs/webpack.prod.cjs
module.exports = {
  entry: {
    storyteller: './src/index.ts',
    'storyteller.page': './src/storyPage.index.ts',
    'storyteller.newfeature': './src/newfeature/index.ts', // New entry
  },
};
```

### Customizing Bundle Output

```javascript
// For web distribution
output: {
  path: path.join(__dirname, 'dist'),
  filename: '[name].js',
  library: ['Storyteller'],
  libraryTarget: 'var'          // Global variable
}

// For NPM package
output: {
  chunkFilename: '[name].min.js'
}
```

### Optimizing Bundle Size

```javascript
// Use Preact instead of React
resolve: {
  alias: {
    'react': 'preact/compat',
    'react-dom': 'preact/compat',
    'react/jsx-runtime': 'preact/jsx-runtime'
  }
}

// Minification settings
optimization: {
  minimizer: [
    new TerserPlugin({ extractComments: false })
  ]
}
```

## Patterns & Conventions

### DO:

- **Use CommonJS format** for webpack configs (`.cjs`)
- **Share common config** via `webpack-merge`
- **Define environment variables** through DefinePlugin
- **Extract CSS in production** for caching
- **Use source maps in development** only
- **Alias React to Preact** for smaller bundles
- **Monitor bundle size** with analyze script

### DON'T:

- **Don't include source maps** in production
- **Don't bundle node_modules** unnecessarily
- **Don't forget CircularDependencyPlugin** - prevents issues
- **Don't hardcode environment values** - use env vars
- **Don't split vendor chunks** - single bundle strategy
- **Don't use ES modules** in webpack configs

## Module Rules

### TypeScript/TSX Files

```javascript
{
  test: /\.tsx?$/,
  loader: 'ts-loader',
  exclude: /node_modules/
}
```

### JavaScript/JSX Files

```javascript
{
  test: /\.jsx?$/,
  exclude: /(node_modules)/,
  loader: 'babel-loader',
  options: {
    presets: ['@babel/env'],
  },
}
```

### SCSS/CSS Processing

```javascript
// Development - inject styles
{
  test: /\.s?css$/,
  use: [
   {
    loader: 'style-loader', // Inject into DOM
    options: {
      insert: 'head',
      },
    },
    {
      loader: 'css-loader',
      options: {
        modules: {
          auto: /\.module\.s?css$/, // Auto-detect modules
          localIdentName: '[local]-[hash:base64:6]',
        },
      },
    },
    'sass-loader', // SCSS compilation
    'postcss-loader', // Autoprefixer
  ],
},

// Production - extract to files
{
  test: /\.s?css$/,
  use: [
    { loader: MiniCssExtractPlugin.loader },
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[local]-[hash:base64:6]',
        },
      },
    },
    'sass-loader',
    'postcss-loader',
  ],
},
```

## Plugin Configuration

### DefinePlugin - Environment Variables

```javascript
new webpack.DefinePlugin({
  VERSION: JSON.stringify(require('./package.json').version),
  API_ENVIRONMENT: JSON.stringify(process.env.API_ENVIRONMENT || 'production'),
  AMP_BASE_URL: JSON.stringify(
    process.env.AMP_BASE_URL || 'https://stories.usestoryteller.com/amp'
  ),
  FEATURE_FLAG: JSON.stringify(process.env.FEATURE_FLAG || false),
});
```

### MiniCssExtractPlugin - CSS Extraction

```javascript
new MiniCssExtractPlugin({
  filename: '[name].css',
  chunkFilename: '[name].css',
});
```

### CircularDependencyPlugin - Prevent Circular Deps

```javascript
new CircularDependencyPlugin({
  exclude: /node_modules/,
  failOnError: true,
  cwd: process.cwd(),
});
```

### ModuleFederationPlugin - Development Only

```javascript
new ModuleFederationPlugin({
  name: 'webSdk',
  filename: 'remoteEntry.js',
  exposes: {
    '.': './src/index.npm.ts',
  },
});
```

## Development Configuration

### webpack.dev.cjs

```javascript
const { merge } = require('webpack-merge');
const common = require('./webpack.common.cjs');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'source-map',
  watchOptions: {
    ignored: /dist/,
  },
  plugins: [
    new ModuleFederationPlugin({
      // Enable micro-frontend capabilities
    }),
  ],
});
```

### Development Server

```javascript
// browser-sync configuration (dev-proxy-server.cjs)
browserSync.init({
  proxy: 'http://localhost:8080',
  files: ['src/**.js'],
});
```

## Production Configuration

### webpack.prod.cjs

```javascript
const { merge } = require('webpack-merge');
const common = require('./webpack.common.cjs');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin({ extractComments: false })],
    splitChunks: {
      cacheGroups: {
        vendors: false, // Single bundle strategy
      },
    },
  },
});
```

## NPM Package Configuration

### webpack.npm.cjs

```javascript
module.exports = merge(prodConfig, {
  mode: 'production',
  output: {
    libraryTarget: 'commonjs-static', // For NPM
  },
  module: {
    rules: [
      {
        // Remove type-only imports for backwards compatibility
        test: /\.tsx?$/,
        loader: 'string-replace-loader',
        options: {
          search: /import[^;]*(type)[^;]*[;]/g,
          replace(stringToReplace, p1) {
            const newString = stringToReplace.replace(/\btype\b\s*/g, '');

            return newString;
          },
        },
      },
    ],
  },
});
```

### Package.json Exports

```json
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./index.cjs"
    },
    "./dist/storyteller.min.css": "./dist/storyteller.min.css"
  }
}
```

## Optimization Strategies

### Bundle Size Optimization

1. **Preact Instead of React**:

   - Saves ~40KB gzipped
   - Full React compatibility via aliases

2. **Single Bundle Strategy**:

   - No vendor splitting
   - Better for SDK distribution

3. **Minification**:
   - Terser with aggressive settings

## Testing & Analysis

### Bundle Analysis

```bash
# Analyze bundle composition
npm run analyze:npm

# Opens webpack-bundle-analyzer
# Shows module sizes and dependencies
```

### Build Performance

```bash
# Measure build time
time npm run build

# Watch mode performance
npm run watch -- --profile
```

## Gotchas & Important Notes

### Critical Gotchas:

1. **CommonJS configs** - Use `.cjs` extension for Node.js
2. **Preact aliasing** - Must alias all React imports
3. **CSS modules** - Auto-detection via `.module.scss`
4. **Circular dependencies** - Will fail build if detected
5. **Source maps** - Never include in production

### TypeScript Considerations:

- Declaration files generated in `dist/`
- Type-only imports removed for NPM build
- Strict mode enabled in tsconfig

### Environment Variables:

- Set at build time, not runtime
- Use DefinePlugin for injection
- Different per environment

## Dependencies

### Build Dependencies:

- `webpack`: 5.x
- `typescript`: 4.x
- `sass`: SCSS compilation
- `postcss`: CSS processing
- `babel`: JavaScript transpilation

### Key Plugins:

- `terser-webpack-plugin`: Minification
- `mini-css-extract-plugin`: CSS extraction
- `circular-dependency-plugin`: Dependency validation
- `webpack-bundle-analyzer`: Bundle analysis

## Related Files

### Configuration:

- `/tsconfig.json` - TypeScript settings
- `/postcss.config.cjs` - PostCSS configuration
- `/package.json` - Build scripts

### Documentation:

- `/CLAUDE.md` - Project overview
- `/src/CLAUDE.md` - Source architecture
- `/demo/CLAUDE.md` - Demo configuration

## Quick Reference

### Build Commands:

```bash
# Development
npm run watch         # Watch SDK
npm run start         # Full dev environment

# Production
npm run build         # Web distribution
npm run build:npm     # NPM package

# Analysis
npm run analyze:npm   # Bundle analyzer
```

### Key Configurations:

```javascript
// Preact aliasing
alias: {
  'react': 'preact/compat',
  'react-dom': 'preact/compat'
}

// Output formats
libraryTarget: 'var'             // Web
libraryTarget: 'commonjs-static' // NPM

// Entry points
entry: {
  'storyteller': './src/index.ts',
  'storyteller.page': './src/storyPage.index.ts'
}
```

### Debug Helpers:

```bash
# Verbose webpack output
npm run build -- --verbose

# Profile build performance
npm run build -- --profile

# Debug webpack config
node -e "console.log(require('./webpack.prod.cjs'))"
```
