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:
rm -rf node_modules/ zip -r my-plugin-1.0.0.zip my-plugin/Exclude src/ if dist/ exists:
zip -r my-plugin-1.0.0.zip my-plugin/ -x "my-plugin/src/*"Minimize dependencies:
# Remove unused packages npm prune --productionCheck for large assets:
find 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.