Monorepo Reference
Purpose: For contributors, provides workspace layout, pnpm commands, CI/CD pipeline, and troubleshooting.
Workspace Layout
headlamp-branding-plugin/
├── plugins/
│ └── branding/ # @opencenter/headlamp-plugin-branding
│ ├── src/
│ │ ├── index.tsx # Plugin entry point (registerAppLogo, registerAppTheme)
│ │ ├── components/
│ │ │ └── AppLogo.tsx # Logo component (light/dark, small/large)
│ │ ├── theme/
│ │ │ ├── index.ts # Re-exports both themes
│ │ │ ├── color.ts # Hex/RGB conversion and blending utilities
│ │ │ ├── opencenter.light.ts # Cloud Day theme definition
│ │ │ └── opencenter.dark.ts # Abyssal Night theme definition
│ │ └── setupTests.ts # Jest DOM matchers
│ ├── assets/
│ │ ├── logo.png # Light theme logo
│ │ └── logo_dark.png # Dark theme logo
│ ├── __tests__/ # Root-level property tests
│ ├── e2e/ # Playwright end-to-end tests
│ ├── dist/ # Build output (generated)
│ ├── package.json
│ ├── tsconfig.json
│ ├── webpack.config.js
│ ├── jest.config.js
│ ├── playwright.config.ts
│ ├── .eslintrc.js
│ └── .prettierrc
├── .github/
│ └── workflows/ # CI/CD workflows
├── package.json # Workspace root (scripts delegate to plugins)
├── pnpm-workspace.yaml # Declares plugins/* as workspace packages
├── pnpm-lock.yaml # Single lockfile for all packages
└── .npmrc # pnpm configuration
pnpm Workspace Configuration
pnpm-workspace.yaml declares the workspace packages:
packages:
- 'plugins/*'
All plugins under plugins/ are automatically discovered. The root package.json defines workspace-level scripts that delegate to all packages.
Commands Reference
Workspace-Wide Commands
| Command | Description |
|---|---|
pnpm install | Install dependencies for all packages |
pnpm run build | Build all plugins |
pnpm test | Run tests across all plugins |
pnpm run lint | Lint all plugins |
pnpm run format | Format all plugins with Prettier |
pnpm run format:check | Check formatting without writing |
Plugin-Specific Commands
Use --filter to target a single plugin:
| Command | Description |
|---|---|
pnpm --filter @opencenter/headlamp-plugin-branding run dev | Webpack watch mode |
pnpm --filter @opencenter/headlamp-plugin-branding run build | Production build |
pnpm --filter @opencenter/headlamp-plugin-branding test | Run Jest tests |
pnpm --filter @opencenter/headlamp-plugin-branding run test:coverage | Tests with coverage report |
pnpm --filter @opencenter/headlamp-plugin-branding run test:e2e | Playwright E2E tests |
pnpm --filter @opencenter/headlamp-plugin-branding run lint | ESLint check |
pnpm --filter @opencenter/headlamp-plugin-branding run lint:fix | ESLint auto-fix |
pnpm --filter @opencenter/headlamp-plugin-branding run package | Create distributable ZIP |
Build Toolchain
| Tool | Version | Purpose |
|---|---|---|
| Node.js | >= 20.0.0 | Runtime |
| pnpm | >= 9.0.0 | Package manager |
| TypeScript | ^5.7 | Type checking |
| webpack | ^5.97 | Bundling (main.js as CommonJS2) |
| ts-loader | ^9.5 | TypeScript compilation in webpack |
| copy-webpack-plugin | ^14.0 | Copies package.json and assets/ to dist/ |
| Jest | ^29.7 | Unit and property tests |
| fast-check | ^4.5 | Property-based testing |
| Playwright | ^1.58 | End-to-end browser tests |
| ESLint | ^9.18 | Linting |
| Prettier | ^3.4 | Code formatting |
Webpack Externals
The webpack config (plugins/branding/webpack.config.js) marks these as externals — they are provided by the Headlamp host at runtime:
externals: {
react: 'react',
'react-dom': 'react-dom',
'@kinvolk/headlamp-plugin/lib': '@kinvolk/headlamp-plugin/lib',
}
Do not bundle these. They must match the versions shipped with Headlamp.
CI/CD Pipeline
On every pull request, GitHub Actions runs:
pnpm install— dependency installationpnpm run lint— ESLint checkspnpm run format:check— Prettier formatting checkpnpm test— Jest unit and property testspnpm run build— webpack production buildpnpm audit— dependency security audit
On tag push (v*), the pipeline additionally packages the plugin as a ZIP and creates a GitHub release with the artifact attached.
Adding a New Plugin
- Create the plugin directory:
mkdir -p plugins/my-plugin/src - Add a
package.jsonwith the@opencenter/headlamp-plugin-prefix,peerDependencieson@kinvolk/headlamp-plugin,react, andreact-dom. - Add a
webpack.config.jsfollowing the same externals pattern as the branding plugin. - Run
pnpm installfrom the workspace root — the new package is auto-discovered. - The plugin will be included in all workspace-wide commands.
Troubleshooting
pnpm install fails with resolution errors — Clear the store and reinstall:
pnpm store prune
rm -rf node_modules pnpm-lock.yaml plugins/*/node_modules
pnpm install
Workspace command does not find a plugin — Verify the plugin directory is under plugins/ and has a valid package.json with a name field.
TypeScript errors during build — Run pnpm --filter <plugin> run lint to surface type issues. Check that tsconfig.json extends the correct base and includes all source files.
Jest cannot find modules — Confirm jest.config.js has the correct moduleNameMapper entries and that ts-jest is configured as the transform.