What is the Final Plugin Package?
A BrainDrive plugin is distributed as a ZIP file that users can install via the Plugin Installer UI.
ZIP Contents
my-plugin-1.0.0.zip
└── my-plugin/ # Plugin folder name
├── package.json # npm metadata
├── lifecycle_manager.py # Backend lifecycle hooks
├── webpack.config.js # Build configuration
├── tsconfig.json # TypeScript configuration
├── src/ # Source code
│ ├── index.tsx
│ ├── MyPlugin.tsx
│ ├── MyPlugin.css
│ ├── types.ts
│ ├── components/
│ ├── services/
│ └── utils/
├── public/ # Static assets
│ └── icon.png
├── dist/ # Build output (MUST exist)
│ ├── remoteEntry.js # Module Federation entry (REQUIRED)
│ ├── main.js
│ └── ... other chunks
├── README.md # Installation/usage instructions
├── LICENSE # License file
├── .npmignore # Files to exclude from npm (if publishing)
└── (optional) CHANGELOG.md
ZIP File Naming
Format
{plugin-slug}-{version}.zip
Examples
✅ VALID - my-plugin-1.0.0.zip - task-manager-0.1.0.zip - data-dashboard-2.3.1.zip ❌ INVALID - my-plugin.zip (missing version) - my-plugin-v1.0.0.zip (has "v" prefix) - my-plugin-latest.zip (not semver) - my_plugin-1.0.0.zip (uses underscore)
Build Output Requirements
MUST Include: dist/ Folder
Before creating the ZIP, run the build:
npm install npm run build
This creates the dist/ folder with:
- •dist/remoteEntry.js (REQUIRED) - Module Federation entry point
- •dist/main.js - Your plugin code bundle
- •dist/{other-chunks}.js - Split code chunks if any
MUST Exist Before ZIP
# Verify dist folder and remoteEntry.js exist ls -la dist/ # Output: # -rw-r--r-- main.js # -rw-r--r-- remoteEntry.js
Build Configuration (webpack.config.js)
const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
mode: 'production',
entry: './src/index.tsx',
output: {
path: __dirname + '/dist',
filename: 'main.js',
publicPath: 'auto' // Critical for Module Federation
},
plugins: [
new ModuleFederationPlugin({
name: 'MyPlugin', // Must match plugin_slug
filename: 'remoteEntry.js', // MUST be exactly this
exposes: {
'./index': './src/index.tsx'
},
shared: {
react: { singleton: true, requiredVersion: '^18.2.0' },
'react-dom': { singleton: true, requiredVersion: '^18.2.0' }
}
})
]
};
Step-by-Step Packaging Process
Step 1: Verify Source Code
# Check that all source files exist ls -la # Files MUST exist: # - package.json # - lifecycle_manager.py # - webpack.config.js # - tsconfig.json # - src/index.tsx
Step 2: Install Dependencies
npm install
Step 3: Build for Production
npm run build
Verify build output:
ls -la dist/ # Must show: # remoteEntry.js # main.js
Step 4: Verify dist/ Folder Size
du -sh dist/ # Typical plugin: 100KB - 500KB # If > 2MB: check for large dependencies or assets
Step 5: Create ZIP File
Using command line:
# Option 1: Include dist/ zip -r my-plugin-1.0.0.zip my-plugin/ # Option 2: Manual folder structure mkdir package_dir cp -r my-plugin/* package_dir/ cd package_dir zip -r ../my-plugin-1.0.0.zip . cd .. rm -rf package_dir
Using Node.js:
const archiver = require('archiver');
const fs = require('fs');
const output = fs.createWriteStream('my-plugin-1.0.0.zip');
const archive = archiver('zip', { zlib: { level: 9 } });
output.on('close', () => {
console.log('ZIP created:', archive.pointer() + ' total bytes');
});
archive.pipe(output);
archive.directory('my-plugin/', 'my-plugin');
archive.finalize();
Step 6: Verify ZIP Contents
# List ZIP contents unzip -l my-plugin-1.0.0.zip # Should show structure like: # my-plugin-1.0.0/my-plugin/package.json # my-plugin-1.0.0/my-plugin/lifecycle_manager.py # my-plugin-1.0.0/my-plugin/dist/remoteEntry.js # ... etc
Step 7: Store ZIP
- •For distribution: Upload to plugin marketplace
- •For installation: Keep ZIP file available locally
- •For backup: Store with version tag
What NOT to Include in ZIP
❌ Exclude These:
- •
node_modules/(too large, users will run npm install) - •
.git/(version control files) - •
.gitignore - •
.env(sensitive data) - •
src/(source code, not needed for runtime) - •
*.log(log files) - •
.DS_Store(macOS metadata) - •
Thumbs.db(Windows metadata) - •
dist/if it's pre-built (users may build differently)
❌ Actually, reconsider dist/:
- •Pro: Users don't need to build, faster install
- •Con: Large ZIP, platform-specific build artifacts
- •Recommendation: Include
dist/for end users, exclude for developers
.npmignore or Similar
If you want different files for npm publishing vs plugin distribution, create .npmignore:
# .npmignore dist/ node_modules/ .git .env *.log screenshot/ docs/
Installation Instructions (Include in README.md)
## Installation 1. Download the plugin ZIP file 2. Open BrainDrive 3. Go to Settings → Plugins → Install Plugin 4. Select the ZIP file 5. BrainDrive will install the plugin and reload 6. Plugin appears in the sidebar / main menu ## Building from Source If you have the source code: ```bash npm install npm run build zip -r my-plugin-1.0.0.zip my-plugin/
Then follow installation steps above.
## Validation Before Packaging Run this checklist: ```bash # ✅ Verify essential files exist [ -f package.json ] && echo "✓ package.json" [ -f lifecycle_manager.py ] && echo "✓ lifecycle_manager.py" [ -f webpack.config.js ] && echo "✓ webpack.config.js" [ -f tsconfig.json ] && echo "✓ tsconfig.json" [ -d src ] && echo "✓ src/ folder" [ -d dist ] && echo "✓ dist/ folder" [ -f dist/remoteEntry.js ] && echo "✓ dist/remoteEntry.js" # ✅ Verify version consistency echo "package.json version:" grep '"version"' package.json echo "lifecycle_manager.py version:" grep '"version"' lifecycle_manager.py # ✅ Verify plugin_slug matches scope echo "plugin_slug:" grep 'plugin_slug' lifecycle_manager.py echo "webpack scope:" grep "name: '" webpack.config.js # ✅ Verify no sensitive data ! grep -r 'API_KEY\|SECRET\|PASSWORD' src/ && echo "✓ No hardcoded secrets" ! [ -f .env ] && echo "✓ No .env file"
ZIP Size Guidelines
| Size | Status | Recommendation |
|---|---|---|
| < 100 KB | ✅ Excellent | Small, fast download |
| 100 KB - 500 KB | ✅ Good | Typical for feature-rich plugins |
| 500 KB - 2 MB | ⚠️ Large | Consider optimizing dependencies |
| > 2 MB | ❌ Too Large | Likely includes unnecessary files |
Optimization Tips
If your ZIP is too large:
- •
Remove node_modules before zipping:
bashrm -rf node_modules/ zip -r my-plugin-1.0.0.zip my-plugin/
- •
Exclude src/ if dist/ exists:
bashzip -r my-plugin-1.0.0.zip my-plugin/ -x "my-plugin/src/*"
- •
Minimize dependencies:
bash# Remove unused packages npm prune --production
- •
Check for large assets:
bashfind dist/ -size +100k
Common Packaging Mistakes
❌ Missing dist/remoteEntry.js
# Forgot to build! npm run build # Fix this first
❌ Version mismatch
# package.json: 1.0.0 # lifecycle_manager.py: 1.0.1 # ZIP name: my-plugin-1.0.2.zip # FIX: Make all three match!
❌ Wrong scope in webpack
// plugin_slug: "MyPlugin" // webpack scope: "DifferentScope" // WRONG!
❌ Including node_modules
# This makes ZIP huge! # Always exclude node_modules
Re-Packaging for Updates
When releasing a new version:
- •Update version in package.json, lifecycle_manager.py, changelog
- •Rebuild:
npm run build - •Create new ZIP:
{slug}-{new-version}.zip - •Keep old ZIP for version history
- •Update plugin manifest/changelog
Packaging is the final step. Get it right, and users can install your plugin with a single click.