Package.json Manager is a VS Code extension that provides a visual interface for managing and visualizing package.json files. The extension follows a layered architecture pattern with clear separation of concerns.
src/
├── extension.ts # Extension entry point and registration
├── commands/ # Command handlers
│ ├── OpenEditorCommand.ts
│ ├── ToggleViewCommand.ts
│ └── ShowGraphCommand.ts
├── panels/ # Webview panels and custom editors
│ ├── PackageJsonEditorProvider.ts
│ └── DependencyGraphPanel.ts
├── services/ # Business logic layer
│ ├── NpmRegistryService.ts
│ ├── PackageJsonService.ts
│ ├── DependencyService.ts
│ └── FileSystemService.ts
├── utils/ # Shared utilities
│ ├── webviewUtils.ts
│ └── HtmlTemplateBuilder.ts
├── config/ # Configuration management
│ └── ConfigurationManager.ts
└── test/ # Test files
└── suite/
Responsibilities:
- Register commands, providers, and services
- Set up dependency injection
- Minimal business logic
- Act as the composition root
Key Points:
- Keeps activation function under 50 lines
- Delegates all business logic to commands and services
- Manages extension lifecycle
Responsibilities:
- Handle VS Code command execution
- Orchestrate service calls
- Manage user interactions
- Error handling and user feedback
Commands:
OpenEditorCommand: Opens package.json in custom editorToggleViewCommand: Switches between visual and text editorShowGraphCommand: Displays dependency graph visualization
Pattern:
Each command is a class with an execute() method that:
- Resolves necessary URIs/parameters
- Calls appropriate services
- Handles errors with user-friendly messages
Responsibilities:
- Business logic implementation
- External API interactions
- Data transformation
- No direct VS Code UI interactions
Services:
- Interacts with npm registry API
- Searches for packages
- Fetches package details
- Handles rate limiting and errors
- Reads and parses package.json files
- Updates package.json with proper formatting
- Manages dependencies and scripts
- Validates package.json structure
- Generates dependency graph data
- Analyzes node_modules structure
- Detects circular dependencies
- Filters and transforms graph data
- Abstracts file system operations
- Provides async file I/O
- Handles errors consistently
- Enables testability through mocking
Responsibilities:
- Webview lifecycle management
- Message passing between webview and extension
- HTML generation (delegates to template builders)
- State synchronization
Panels:
- Custom text editor for package.json files
- Provides visual editing interface
- Syncs changes between visual and text views
- ~180 lines (reduced from 334)
- Displays interactive dependency graph
- Uses D3.js for visualization
- Fetches package details on demand
- ~220 lines (reduced from 528)
Design Principles:
- Keep panels under 200 lines
- Delegate HTML generation to
HtmlTemplateBuilder - Use services for all business logic
- Implement proper disposal patterns
Responsibilities:
- Pure functions and helpers
- No side effects
- Fully testable
- Reusable across the codebase
Utilities:
- Nonce generation for CSP
- Content Security Policy helpers
- Webview resource URI handling
- Message validation and routing
- Resource management
- Template-based HTML generation
- Component builders (buttons, tabs, forms)
- Pre-configured builders for specific views
- Consistent HTML structure
Responsibilities:
- Type-safe configuration access
- Configuration change listeners
- Validation
- Provides type-safe access to extension settings
- Watches for configuration changes
- Validates configuration values
- Exposes convenience methods for common settings
User Action
↓
OpenEditorCommand.execute()
↓
VS Code API (vscode.openWith)
↓
PackageJsonEditorProvider.resolveCustomTextEditor()
↓
PackageJsonService.readPackageJson()
↓
HTML generated via HtmlTemplateBuilder
↓
Webview displayed to user
User Action
↓
ShowGraphCommand.execute()
↓
DependencyGraphPanel.createOrShow()
↓
DependencyService.generateDependencyGraph()
├→ FileSystemService.readJsonFile()
└→ Recursive dependency analysis
↓
HTML generated with graph data
↓
D3.js renders interactive visualization
Webview: User types search query
↓
Message sent to extension
↓
WebviewMessageRouter.handle()
↓
NpmRegistryService.searchPackages()
↓
Results sent back to webview
↓
Webview displays results
The extension uses constructor injection for all dependencies:
class PackageJsonEditorProvider {
private readonly npmService: NpmRegistryService;
private readonly packageJsonService: PackageJsonService;
constructor(context: vscode.ExtensionContext) {
const fileSystem = new FileSystemService();
this.npmService = new NpmRegistryService();
this.packageJsonService = new PackageJsonService(fileSystem);
}
}Benefits:
- Testability: Easy to inject mocks
- Flexibility: Can swap implementations
- Clarity: Dependencies are explicit
-
Service Layer: Throws typed errors
throw new NpmRegistryError('Package not found', packageName);
-
Command Layer: Catches, logs, and shows user feedback
catch (error) { vscode.window.showErrorMessage(`Failed: ${error.message}`); console.error('Command failed:', error); }
-
Panel Layer: Handles webview-specific errors
webview.postMessage({ command: 'error', message: error.message });
NpmRegistryError: npm API failuresPackageJsonError: package.json operationsFileSystemError: File I/O failures
All webviews use strict CSP:
default-src 'none': Deny all by defaultscript-src 'nonce-${nonce}': Only scripts with noncestyle-src ${cspSource} 'unsafe-inline': Styles from extension- No
'unsafe-eval'or arbitrary script execution
All messages from webview are validated:
function isValidWebviewMessage(msg: unknown): msg is WebviewMessage {
return (
typeof msg === 'object' &&
msg !== null &&
'command' in msg &&
typeof (msg as any).command === 'string'
);
}File paths are validated to prevent directory traversal:
if (!fullPath.startsWith(workspaceRoot)) {
throw new Error('Path outside workspace');
}- Lazy Loading: Heavy dependencies loaded on demand
- Activation Events: Extension activates only when needed
- Caching: Expensive operations cached where appropriate
- Resource Disposal: Proper cleanup prevents memory leaks
- Webview Context:
retainContextWhenHiddenused sparingly
- All service classes
- Utility functions
- Pure business logic
- Command execution
- Webview communication
- Panel lifecycle
- Full user workflows
- Extension activation
- Multi-file scenarios
Extension provides these settings:
packageJsonManager.enableAutomaticVisualEditing: Auto-open in visual modepackageJsonManager.showDependencyGraphButton: Show/hide graph buttonpackageJsonManager.maxDependencyDepth: Maximum graph depth (1-10)packageJsonManager.defaultViewMode: Default view mode (visual/text)
- TypeScript compilation
- Webpack bundling
- VSIX packaging
- Marketplace publication
- PR Validation: Lint, test, build
- Security Scan: npm audit, CodeQL
- Release: Automated on tag push
- Testing: Expand test coverage to 80%+
- Performance: Bundle size optimization
- Features: Package update notifications
- UX: Improved error messages
- Analytics: Usage telemetry
- Add comprehensive unit tests
- Implement e2e testing framework
- Add performance benchmarks
- Document webview JavaScript
When contributing, please:
- Follow the established architecture
- Add tests for new features
- Update documentation
- Run linting and formatting
- Keep services under 300 lines
- Keep panels under 200 lines