vendure-plugin-reviewing
Vendure Plugin Reviewing
Purpose
Audit Vendure plugins for violations of best practices, security issues, and anti-patterns.
When NOT to Use
- Creating new plugins (use vendure-plugin-writing)
- GraphQL-only review (use vendure-graphql-reviewing)
- Entity-only review (use vendure-entity-reviewing)
Review Workflow
Step 1: Identify Plugin Files
# Find all plugin files
find . -name "*.plugin.ts" -o -name "*-plugin.ts"
# Find all service files
find . -name "*.service.ts"
# Find all entity files
find . -name "*.entity.ts"
Step 2: Run Automated Checks
# === CRITICAL VIOLATIONS ===
# Missing @VendurePlugin decorator
grep -rn "export class.*Plugin" --include="*plugin.ts" | grep -v "@VendurePlugin"
# Missing PluginCommonModule import
grep -rn "@VendurePlugin" --include="*plugin.ts" -A 10 | grep -v "PluginCommonModule"
# Direct database access (bypassing TransactionalConnection)
grep -rn "getRepository\|createQueryBuilder\|getConnection" --include="*.service.ts" | grep -v "TransactionalConnection"
# === HIGH PRIORITY ===
# Missing @Injectable on services
grep -rn "export class.*Service" --include="*.service.ts" | grep -v "@Injectable"
# Hardcoded configuration values
grep -rn "localhost\|127.0.0.1\|apiKey.*=.*['\"]" --include="*.plugin.ts" --include="*.service.ts"
# Missing RequestContext parameter
grep -rn "async.*\(" --include="*.service.ts" | grep -v "ctx.*RequestContext\|RequestContext.*ctx"
# === MEDIUM PRIORITY ===
# Missing init() static method
grep -rn "export class.*Plugin" --include="*plugin.ts" -A 20 | grep -v "static init"
# Missing default options
grep -rn "this.options = options" --include="*plugin.ts" | grep -v "...default\|...options"
# Missing lifecycle cleanup
grep -rn "onApplicationBootstrap" --include="*plugin.ts" | xargs -I{} sh -c 'grep -L "onApplicationShutdown" {} 2>/dev/null'
Step 3: Manual Review Checklist
Plugin Structure
- @VendurePlugin decorator present
- PluginCommonModule in imports
- All entities in entities array
- All services in providers array
- Static init() method with defaults
- Options interface with types
Services
- @Injectable() decorator on all services
- RequestContext as first parameter
- TransactionalConnection for DB access
- No direct TypeORM repository access
- Proper error handling
Lifecycle
- OnApplicationBootstrap if initialization needed
- OnApplicationShutdown if cleanup needed
- Async/await on lifecycle methods
- Strategy init() calls in bootstrap
Configuration
- Options interface defined
- Default values for all options
- No hardcoded secrets
- Environment variable usage for sensitive data
Severity Classification
CRITICAL (Must Fix Before Merge)
- Missing @VendurePlugin decorator
- Direct database access bypassing services
- Missing PluginCommonModule import
- Hardcoded secrets or API keys
- Security vulnerabilities
HIGH (Should Fix Before Merge)
- Missing @Injectable decorator
- Missing RequestContext in service methods
- Hardcoded configuration values
- Missing error handling
- No default options
MEDIUM (Should Fix)
- Missing lifecycle cleanup (shutdown)
- No configuration validation
- Poor naming conventions
- Missing TypeScript types
LOW (Suggestions)
- Missing documentation
- Could use better error messages
- Performance improvements possible
Review Output Template
## Vendure Plugin Review: [Plugin Name]
### Summary
[Brief overview of plugin purpose and review findings]
### Critical Issues (Must Fix)
- [ ] [Issue description] - `file:line`
### High Priority
- [ ] [Issue description] - `file:line`
### Medium Priority
- [ ] [Issue description] - `file:line`
### Passed Checks
- [x] @VendurePlugin decorator present
- [x] PluginCommonModule imported
- [x] All services @Injectable
### Recommendations
- [Improvement suggestions]
Common Violations
1. Missing PluginCommonModule
Violation:
@VendurePlugin({
imports: [], // Missing PluginCommonModule!
providers: [MyService],
})
Fix:
@VendurePlugin({
imports: [PluginCommonModule],
providers: [MyService],
})
2. Direct Database Access
Violation:
@Injectable()
export class MyService {
constructor(
@InjectRepository(MyEntity) // WRONG
private repo: Repository<MyEntity>,
) {}
}
Fix:
@Injectable()
export class MyService {
constructor(
private connection: TransactionalConnection, // CORRECT
) {}
async findAll(ctx: RequestContext) {
return this.connection.getRepository(ctx, MyEntity).find();
}
}
3. Missing RequestContext
Violation:
async findAll(): Promise<MyEntity[]> { // Missing ctx!
return this.connection.getRepository(MyEntity).find();
}
Fix:
async findAll(ctx: RequestContext): Promise<MyEntity[]> {
return this.connection.getRepository(ctx, MyEntity).find();
}
4. Hardcoded Options
Violation:
static init(options: MyOptions): typeof MyPlugin {
this.options = options; // No defaults!
return MyPlugin;
}
Fix:
static init(options: MyOptions = {}): typeof MyPlugin {
this.options = {
...defaultOptions, // Always merge with defaults
...options,
};
return MyPlugin;
}
5. Strategy Without Lifecycle
Violation:
static init(options: { strategy: MyStrategy }) {
this.options = options;
// Strategy init() never called!
}
Fix:
async onApplicationBootstrap(): Promise<void> {
if (typeof MyPlugin.options.strategy.init === 'function') {
await MyPlugin.options.strategy.init(this.injector);
}
}
Quick Detection Commands
# All-in-one audit script
echo "=== CRITICAL ===" && \
grep -rn "export class.*Plugin" --include="*plugin.ts" | grep -v "@VendurePlugin" && \
echo "=== HIGH ===" && \
grep -rn "export class.*Service" --include="*.service.ts" | grep -v "@Injectable" && \
echo "=== MEDIUM ===" && \
grep -rn "TODO\|FIXME\|HACK" --include="*.ts"
Cross-Reference
All rules in this skill match patterns documented in vendure-plugin-writing skill.
When reviewing, verify the plugin follows the patterns in:
- vendure-plugin-writing (plugin structure)
- vendure-graphql-writing (if has API extensions)
- vendure-entity-writing (if has entities)
- vendure-admin-ui-writing (if has UI extensions)
Related Skills
- vendure-plugin-writing - Plugin creation patterns
- vendure-graphql-reviewing - GraphQL-specific review
- vendure-entity-reviewing - Entity-specific review
- vendure-admin-ui-reviewing - UI-specific review
More from meriley/claude-code-skills
obs-cpp-qt-patterns
C++ and Qt integration patterns for OBS Studio plugins. Covers Qt6 Widgets for settings dialogs, CMAKE_AUTOMOC, OBS frontend API, optional Qt builds with C fallbacks, and modal dialog patterns. Use when adding UI components or C++ features to OBS plugins.
54vendure-developing
Develop Vendure e-commerce plugins, extend GraphQL APIs, create Admin UI components, and define database entities. Use vendure-expert agent for comprehensive guidance across all Vendure development domains.
35vendure-admin-ui-writing
Create Vendure Admin UI extensions with React components, route registration, navigation menus, and GraphQL integration. Handles useQuery, useMutation, useInjector patterns. Use when building Admin UI features for Vendure plugins.
32vendure-entity-writing
Define Vendure database entities extending VendureEntity, with TypeORM decorators, relations, custom fields, and channel-awareness. Use when creating database models in Vendure.
30vendure-graphql-writing
Extend Vendure GraphQL schema with custom types, queries, mutations, and resolvers. Handles RequestContext threading, permissions, and dual Shop/Admin API separation. Use when adding GraphQL endpoints to Vendure.
30vendure-plugin-writing
Create production-ready Vendure plugins with @VendurePlugin decorator, NestJS dependency injection, lifecycle hooks, and configuration patterns. Use when developing new Vendure plugins or extending existing ones.
28