npm package: @kpsoftec/vite-external-remotes-plugin
Description: Vite plugin for dynamic external remotes with window variable resolution
Author: KPSoftec
Latest version: 1.1.0

# vite-external-remotes-plugin

A Vite plugin for dynamic external remotes with window variable resolution in microfrontend applications. Enables runtime configuration of remote URLs for Module Federation.

## Features

- ✅ Dynamic remote URL resolution using window variables
- ✅ Support for multiple pattern formats
- ✅ Runtime window variable replacement
- ✅ Plugin options (debug, variablePrefix, patterns)
- ✅ TypeScript support
- ✅ Debug logging

## Installation

```bash
npm install @kpsoftec/vite-external-remotes-plugin
```

## Basic Usage

```javascript
// vite.config.js
import { defineConfig } from 'vite'
import ExternalRemotesPlugin from '@kpsoftec/vite-external-remotes-plugin'
import federation from '@originjs/vite-plugin-federation'

export default defineConfig({
  plugins: [
    ExternalRemotesPlugin(),
    federation({
      name: 'host-app',
      remotes: {
        remote_app: '@[window.remote_app_url]/assets/remoteEntry.js',
        remote_two: 'remote@[window.remote_two_url]/remoteEntry.js'
      },
      shared: ['react', 'react-dom']
    })
  ]
})
```

## Supported Patterns

| Pattern | Description | Example |
|---------|-------------|---------|
| `@[window.variable]/path` | Direct window variable URL | `@[window.remote_url]/remoteEntry.js` |
| `remote@[window.variable]/path` | Federation remote with window variable | `myRemote@[window.base_url]/remoteEntry.js` |
| `[window.variable]` | Standalone window variable | `[window.full_remote_url]` |
| `@[window.variable]` | Standalone @ syntax | `@[window.remote_entry_url]` |

## Examples

### Basic Remote Configuration

```javascript
federation({
  name: 'host-app',
  remotes: {
    // Simple pattern
    remote_app: '@[window.remote_app_url]/remoteEntry.js',
    
    // With remote name
    user_service: 'userService@[window.user_service_url]/remoteEntry.js',
    
    // Multiple variables in path
    api_service: '@[window.api_base_url]/services/[window.service_name]/remoteEntry.js'
  }
})
```

### With Query Parameters

```javascript
remotes: {
  // Cache busting with build hash
  remote_app: '@[window.remote_url]/remoteEntry.js?v=[window.BUILD_HASH]',
  
  // Multiple parameters
  remote_two: '@[window.api_url]/remoteEntry.js?env=[window.ENV]&v=[window.VERSION]'
}
```

## Runtime Configuration

### Method 1: JSON Configuration File

```javascript
// main.jsx
async function loadRemoteConfig() {
  try {
    const response = await fetch('/config/remotes.json')
    const config = await response.json()
    
    // Set window variables
    Object.assign(window, config)
    
    // Import app after config is loaded
    await import('./bootstrap')
  } catch (error) {
    console.error('Failed to load remote config:', error)
  }
}

loadRemoteConfig()
```

```json
// public/config/remotes.json
{
  "remote_app_url": "http://localhost:4173",
  "user_service_url": "https://user-service.example.com",
  "BUILD_HASH": "abc123def456"
}
```

### Method 2: Environment-based Configuration

```javascript
// config/remotes.js
const configs = {
  development: {
    remote_app_url: 'http://localhost:4001',
    user_service_url: 'http://localhost:4002',
    BUILD_HASH: 'dev'
  },
  production: {
    remote_app_url: 'https://remote1.example.com',
    user_service_url: 'https://user-service.example.com',
    BUILD_HASH: process.env.VITE_BUILD_HASH
  }
}

const env = import.meta.env.MODE || 'development'
Object.assign(window, configs[env])
```

### Method 3: Direct Assignment

```javascript
// Set before app initialization
window.remote_app_url = 'https://my-remote.example.com'
window.BUILD_HASH = '12345'

// Then import your app
import('./App')
```

## Plugin Options

```javascript
// vite.config.js
ExternalRemotesPlugin({
  // Custom variable prefix (default: 'window.')
  variablePrefix: 'globalThis.',
  
  // Enable debug logging
  debug: true,
  
  // Custom pattern matching
  patterns: [
    /\[window\.[\w_]+\]/g,
    /\[globalThis\.[\w_]+\]/g
  ]
})
```

## TypeScript Support

```typescript
// types/window.d.ts
declare global {
  interface Window {
    remote_app_url: string;
    user_service_url: string;
    BUILD_HASH: string;
    ENV: 'development' | 'staging' | 'production';
  }
}

export {};
```

```typescript
// config.ts
import type { ExternalRemotesPluginOptions } from '@kpsoftec/vite-external-remotes-plugin';

interface RemoteConfig {
  remote_app_url: string;
  user_service_url: string;
  BUILD_HASH: string;
}

export async function loadTypedConfig(): Promise<RemoteConfig> {
  const response = await fetch('/config/remotes.json');
  const config: RemoteConfig = await response.json();
  
  // Type-safe assignment
  Object.assign(window, config);
  
  return config;
}
```

## Debug Mode

Enable debug logging to see pattern replacements:

```javascript
ExternalRemotesPlugin({ debug: true })
```

This will log:
- Which files are being processed
- What patterns are being replaced
- Available window variables

## How It Works

The plugin transforms federation configuration at build time by replacing window variable patterns with actual window property access:

**Before transformation:**
```javascript
remotes: {
  remote_app: '@[window.remote_url]/remoteEntry.js?v=[window.BUILD_HASH]'
}
```

**After transformation:**
```javascript
remotes: {
  remote_app: window.remote_url + "/remoteEntry.js?v=" + window.BUILD_HASH
}
```

## Best Practices

1. **Load configuration before app initialization**
   ```javascript
   // ✅ Correct
   await loadConfig()
   await import('./App')
   
   // ❌ Wrong - app loads before config
   import('./App')
   loadConfig()
   ```

2. **Provide fallback values**
   ```javascript
   window.remote_url = window.remote_url || 'https://fallback.example.com'
   ```

3. **Validate required variables**
   ```javascript
   const required = ['remote_app_url', 'BUILD_HASH']
   const missing = required.filter(key => !window[key])
   if (missing.length > 0) {
     throw new Error(`Missing config: ${missing.join(', ')}`)
   }
   ```

## Troubleshooting

### Remote not loading
- Ensure window variables are set before app initialization
- Check browser console for variable values: `console.log(window.remote_app_url)`

### 404 errors
- Verify remote URLs are accessible
- Check if BUILD_HASH or other variables are correctly set

### Pattern not replaced
- Ensure the plugin is loaded before the federation plugin
- Check that patterns match exactly: `[window.variable_name]`

## API Reference

### ExternalRemotesPlugin(options?)

**Parameters:**
- `options.variablePrefix`: `string` - Variable prefix (default: 'window.')
- `options.debug`: `boolean` - Enable debug logging (default: false)
- `options.patterns`: `RegExp[]` - Custom pattern matching

**Returns:** Vite plugin that transforms federation remote configurations

**Supported Patterns:**
- `@[window.variable]/path` → `window.variable + "/path"`
- `remote@[window.variable]/path` → `window.variable + "/path"`
- `[window.variable]` → `window.variable`

## License

MIT