Version: 1.0.0 Best practices for building robust, maintainable Eleva plugins.
Robust error handling ensures plugins fail gracefully and provide meaningful feedback:
const RobustPlugin = {
name: "robust",
version: "1.0.0",
install(eleva, options = {}) {
// Validate required options
if (options.apiKey && typeof options.apiKey !== "string") {
throw new Error("[RobustPlugin] apiKey must be a string");
}
// Validate Eleva instance has required features
if (typeof eleva.mount !== "function") {
throw new Error("[RobustPlugin] Invalid Eleva instance");
}
// Warn about deprecated options (don't throw)
if (options.legacyMode !== undefined) {
console.warn("[RobustPlugin] legacyMode is deprecated, use modernMode instead");
}
// Safe method wrapping with error boundaries
const originalMount = eleva.mount.bind(eleva);
eleva.mount = async function(...args) {
try {
return await originalMount(...args);
} catch (error) {
console.error("[RobustPlugin] Mount failed:", error);
throw error; // Re-throw to maintain expected behavior
}
};
// Add methods with built-in error handling
eleva.robust = {
safeOperation: (data) => {
try {
if (!data) {
console.warn("[RobustPlugin] safeOperation called with no data");
return null;
}
return processData(data);
} catch (error) {
console.error("[RobustPlugin] safeOperation failed:", error);
return null; // Return safe default
}
}
};
}
};
| Scenario | Action |
|---|---|
| Invalid required options | Throw descriptive error |
| Invalid optional options | Use defaults, optionally warn |
| Deprecated options | Warn but continue |
| Runtime errors in plugin methods | Catch, log, return safe default |
| Runtime errors in wrapped methods | Catch, log, re-throw |
| Missing Eleva features | Throw or gracefully degrade |
Handle these common edge cases in your plugin:
const EdgeCasePlugin = {
name: "edgeCase",
version: "1.0.0",
_installed: false, // Track installation state
install(eleva, options = {}) {
// Prevent duplicate installation
if (this._installed) {
console.warn("[EdgeCasePlugin] Already installed, skipping");
return eleva.edgeCase; // Return existing instance
}
// Handle missing optional dependencies gracefully
const hasStore = typeof eleva.store !== "undefined";
if (!hasStore && options.useStore) {
console.warn("[EdgeCasePlugin] Store plugin not found, some features disabled");
}
// Set defaults for all options
const config = {
enabled: options.enabled ?? true,
maxItems: options.maxItems ?? 100,
debug: options.debug ?? false,
...options
};
// Validate option ranges
if (config.maxItems < 1 || config.maxItems > 10000) {
console.warn("[EdgeCasePlugin] maxItems out of range (1-10000), using default");
config.maxItems = 100;
}
eleva.edgeCase = {
config,
// Methods can check enabled state
doSomething: () => {
if (!config.enabled) return;
// ... implementation
}
};
this._installed = true;
return eleva.edgeCase;
},
// Optional: uninstall support
uninstall(eleva) {
if (!this._installed) return;
delete eleva.edgeCase;
this._installed = false;
}
};
eleva- prefix for published plugins (e.g., eleva-router, eleva-store)eleva-my-plugin/
├── src/
│ ├── index.js # Main plugin export
│ ├── index.d.ts # TypeScript declarations
│ └── utils.js # Internal utilities
├── test/
│ └── plugin.test.js # Test suite
├── demo/
│ ├── index.html
│ └── main.js
├── dist/ # Built files
├── package.json
├── vite.config.js
└── README.md
uninstall if neededinstall(eleva, options) {
// Lazy load heavy features
let heavyModule = null;
eleva.myPlugin = {
getHeavyFeature: async () => {
if (!heavyModule) {
heavyModule = await import('./heavy-feature.js');
}
return heavyModule;
}
};
}
eval() and innerHTML with untrusted dataeleva.myPlugin = {
// Bad: vulnerable to XSS
renderBad: (content) => {
container.innerHTML = content;
},
// Good: sanitize or use textContent
renderGood: (content) => {
container.textContent = content;
}
};
Create src/index.d.ts:
import Eleva from 'eleva';
// Plugin interface - every plugin must implement this
export interface ElevaPlugin<TOptions = Record<string, unknown>> {
/** Unique plugin identifier */
name: string;
/** Semantic version string */
version: string;
/** Called when plugin is registered via app.use() */
install(eleva: Eleva, options?: TOptions): void | unknown;
/** Optional cleanup when plugin is removed */
uninstall?(eleva: Eleva): void;
}
// Extend Eleva interface with plugin features
declare module 'eleva' {
interface Eleva {
log: {
info(message: string): void;
warn(message: string): void;
error(message: string): void;
debug(message: string): void;
};
}
}
// Plugin-specific options
export interface LoggerOptions {
level?: 'info' | 'warn' | 'error' | 'debug';
prefix?: string;
}
// Typed plugin export
declare const Logger: ElevaPlugin<LoggerOptions>;
export default Logger;
import type { ElevaPlugin } from './index';
interface MyPluginOptions {
debug?: boolean;
maxItems?: number;
}
const MyPlugin: ElevaPlugin<MyPluginOptions> = {
name: "myPlugin",
version: "1.0.0",
install(eleva, options = {}) {
const { debug = false, maxItems = 100 } = options;
// TypeScript will enforce correct option types
eleva.myPlugin = { debug, maxItems };
}
};
export default MyPlugin;
# Build your plugin
npm run build
# Test the build
npm run preview
# Login to npm
npm login
# Publish your plugin
npm publish
Create a README.md with:
# eleva-my-plugin
A plugin for Eleva that does X.
## Installation
\`\`\`bash
npm install eleva-my-plugin
\`\`\`
## Usage
\`\`\`js
import Eleva from 'eleva';
import MyPlugin from 'eleva-my-plugin';
const app = new Eleva('MyApp');
app.use(MyPlugin, { option: 'value' });
\`\`\`
## Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `option` | `string` | `''` | Description |
## API
### `app.myPlugin.method()`
Description of method.
## License
MIT
| Category | Checklist Item |
|---|---|
| Structure | Has name, version, and install |
| Options | Validates required options, defaults optional ones |
| Errors | Throws descriptive errors for critical failures |
| Warnings | Warns (doesn’t throw) for non-critical issues |
| Cleanup | Implements uninstall if resources need cleanup |
| TypeScript | Includes .d.ts type declarations |
| Testing | Has tests for happy path and edge cases |
| Docs | README with installation, usage, and API |
Remember: The best plugins are those that solve real problems while maintaining Eleva’s philosophy of simplicity and performance.
| ← Development Guide | Back to Overview |