|
| 1 | +# Platform Isolation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This project supports multiple runtime platforms (Browser, Node.js, React Native, and Universal), with separate entry points for each. To ensure the build artifacts work correctly, platform-specific code must not be mixed. |
| 6 | + |
| 7 | +## Naming Convention |
| 8 | + |
| 9 | +Platform-specific files use a suffix pattern: |
| 10 | +- `.browser.ts` - Browser-specific implementation |
| 11 | +- `.node.ts` - Node.js-specific implementation |
| 12 | +- `.react_native.ts` - React Native-specific implementation |
| 13 | +- `.ts` (no suffix) - Universal code (works across all platforms) |
| 14 | + |
| 15 | +## Import Rules |
| 16 | + |
| 17 | +Each platform-specific file can **only** import from: |
| 18 | + |
| 19 | +1. **Universal files** (no platform suffix) |
| 20 | +2. **Same-platform files** (matching platform suffix) |
| 21 | +3. **External packages** (node_modules) |
| 22 | + |
| 23 | +### Examples |
| 24 | + |
| 25 | +✅ **Valid Imports** |
| 26 | + |
| 27 | +```typescript |
| 28 | +// In lib/index.browser.ts |
| 29 | +import { Config } from './shared_types'; // ✅ Universal file |
| 30 | +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ✅ Same platform |
| 31 | +import { uuid } from 'uuid'; // ✅ External package |
| 32 | +``` |
| 33 | + |
| 34 | +```typescript |
| 35 | +// In lib/index.node.ts |
| 36 | +import { Config } from './shared_types'; // ✅ Universal file |
| 37 | +import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ✅ Same platform |
| 38 | +``` |
| 39 | + |
| 40 | +❌ **Invalid Imports** |
| 41 | + |
| 42 | +```typescript |
| 43 | +// In lib/index.browser.ts |
| 44 | +import { NodeRequestHandler } from './utils/http_request_handler/request_handler.node'; // ❌ Different platform |
| 45 | +``` |
| 46 | + |
| 47 | +```typescript |
| 48 | +// In lib/index.node.ts |
| 49 | +import { BrowserRequestHandler } from './utils/http_request_handler/request_handler.browser'; // ❌ Different platform |
| 50 | +``` |
| 51 | + |
| 52 | +## Automatic Validation |
| 53 | + |
| 54 | +Platform isolation is enforced automatically during the build process. |
| 55 | + |
| 56 | +### Running Validation |
| 57 | + |
| 58 | +```bash |
| 59 | +# Run validation manually |
| 60 | +npm run validate-platform-isolation |
| 61 | + |
| 62 | +# Validation runs automatically before build |
| 63 | +npm run build |
| 64 | +``` |
| 65 | + |
| 66 | +### How It Works |
| 67 | + |
| 68 | +The validation script (`scripts/validate-platform-isolation.js`): |
| 69 | + |
| 70 | +1. Scans all source files in the `lib/` directory |
| 71 | +2. Identifies platform-specific files by their suffix |
| 72 | +3. Parses import statements (ES6 imports, require, dynamic imports) |
| 73 | +4. Checks that each import follows the platform isolation rules |
| 74 | +5. Fails the build if violations are found |
| 75 | + |
| 76 | +### Build Integration |
| 77 | + |
| 78 | +The validation is integrated into the build process: |
| 79 | + |
| 80 | +```json |
| 81 | +{ |
| 82 | + "scripts": { |
| 83 | + "build": "npm run validate-platform-isolation && tsc --noEmit && ..." |
| 84 | + } |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +If platform isolation is violated, the build will fail with a detailed error message showing: |
| 89 | +- Which files have violations |
| 90 | +- The line numbers of problematic imports |
| 91 | +- What platform the file belongs to |
| 92 | +- What platform it's incorrectly importing from |
| 93 | + |
| 94 | +## Creating New Platform-Specific Code |
| 95 | + |
| 96 | +When creating new platform-specific implementations: |
| 97 | + |
| 98 | +1. Name the file with the appropriate platform suffix (e.g., `my-feature.browser.ts`) |
| 99 | +2. Only import from universal or same-platform files |
| 100 | +3. Create a universal factory or interface if multiple platforms need different implementations |
| 101 | + |
| 102 | +### Example: Creating a Platform-Specific Feature |
| 103 | + |
| 104 | +```typescript |
| 105 | +// lib/features/my-feature.ts (universal interface) |
| 106 | +export interface MyFeature { |
| 107 | + doSomething(): void; |
| 108 | +} |
| 109 | + |
| 110 | +// lib/features/my-feature.browser.ts |
| 111 | +export class BrowserMyFeature implements MyFeature { |
| 112 | + doSomething(): void { |
| 113 | + // Browser-specific implementation |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +// lib/features/my-feature.node.ts |
| 118 | +export class NodeMyFeature implements MyFeature { |
| 119 | + doSomething(): void { |
| 120 | + // Node.js-specific implementation |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +// lib/features/factory.browser.ts |
| 125 | +import { BrowserMyFeature } from './my-feature.browser'; |
| 126 | +export const createMyFeature = () => new BrowserMyFeature(); |
| 127 | + |
| 128 | +// lib/features/factory.node.ts |
| 129 | +import { NodeMyFeature } from './my-feature.node'; |
| 130 | +export const createMyFeature = () => new NodeMyFeature(); |
| 131 | +``` |
| 132 | + |
| 133 | +## Troubleshooting |
| 134 | + |
| 135 | +If you encounter a platform isolation error: |
| 136 | + |
| 137 | +1. **Check the error message** - It will tell you which file and line has the violation |
| 138 | +2. **Identify the issue** - Look at the import statement on that line |
| 139 | +3. **Fix the import**: |
| 140 | + - If the code should be universal, remove the platform suffix from the imported file |
| 141 | + - If the code must be platform-specific, create separate implementations for each platform |
| 142 | + - Use factory patterns to abstract platform-specific instantiation |
| 143 | + |
| 144 | +## Benefits |
| 145 | + |
| 146 | +- ✅ Prevents runtime errors from platform-incompatible code |
| 147 | +- ✅ Catches issues at build time, not in production |
| 148 | +- ✅ Makes platform boundaries explicit and maintainable |
| 149 | +- ✅ Ensures each bundle only includes relevant code |
| 150 | +- ✅ Works independently of linting tools (ESLint, Biome, etc.) |
0 commit comments