This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Essential commands:
npm install- Install dependencies (runstheia-patch,compute-references, and lernaafterInstallhooks)npm run build:browser- Builds all packages + bundles Browser example app (preferred during development)npm run compile- Compile TypeScript only (usestsc --buildwith project references)npm run lint- Run ESLint across all packagesnpm run lint:fix- Run ESLint with auto-fixnpm run test- Run all tests
Important: npm run compile only compiles TypeScript. Before UI testing, you must also run npm run build:browser to bundle the frontend via webpack — otherwise the running browser app won't include your latest changes.
Application commands:
npm run start:browser- Start browser example at localhost:3000npm run start:electron- Start electron applicationnpm run watch- Watch mode for development (browser + electron concurrently)
Package-specific:
npx lerna run compile --scope @theia/package-name- Build specific packagenpx lerna run test --scope @theia/package-name- Test specific packagenpx lerna run watch --scope @theia/package-name --include-filtered-dependencies --parallel- Watch package with dependencies
Running a single test file (after compile):
npx mocha ./packages/core/lib/browser/some-file.spec.js
Test infrastructure: Tests use Mocha + NYC (Istanbul) for coverage. Config at configs/mocharc.yml and configs/nyc.json. Each package's npm test runs via the theiaext test wrapper defined in dev-packages/private-ext-scripts, which executes nyc mocha --config ../../configs/mocharc.yml "./lib/**/*.*spec.js".
Monorepo Structure:
- Lerna-managed monorepo with 77 packages
/packages/- Runtime packages (core + extensions)/dev-packages/- Development tooling (application-manager, cli, eslint-plugin, ext-scripts)/examples/- Sample applications (browser, electron, browser-only, playwright)/configs/- Shared config files (tsconfig, eslint, mocha, nyc)
Platform-specific code organization (per package):
src/common/- Shared JavaScript APIs (runs everywhere)src/browser/- Browser/DOM APIs (InversifyJS DI container for frontend)src/node/- Node.js APIs (InversifyJS DI container for backend)src/electron-browser/- Electron renderer processsrc/electron-main/- Electron main process
Extension entry points are declared in each package's package.json under theiaExtensions:
"theiaExtensions": [{
"frontend": "lib/browser/editor-frontend-module",
"backend": "lib/node/editor-backend-module"
}]Extension System:
- Dependency Injection via InversifyJS (property injection preferred over constructor injection)
- Contribution Points pattern for extensibility (CommandContribution, MenuContribution, KeybindingContribution, FrontendApplicationContribution, etc.)
- Three extension types: Theia extensions (build-time), VS Code extensions (runtime), Theia plugins (runtime)
For more information also look at:
- @doc/coding-guidelines.md
- @doc/Testing.md
- @doc/Plugin-API.md (VS Code extension plugin API)
- @.prompts/project-info.prompttemplate (practical patterns for contributions, widgets, commands, preferences, plugin API, styling)
Code Style:
- 4 spaces indentation, single quotes,
undefinedovernull - PascalCase for types/enums, camelCase for functions/variables
- Arrow functions preferred, explicit return types required
- Property injection over constructor injection,
@postConstruct()for initialization
File Naming:
- kebab-case for files (e.g.,
document-provider.ts) - File name matches main exported type
- Platform folders follow strict dependency rules (browser cannot import node, etc.)
Architecture Patterns:
- Main-Ext pattern for plugin API (browser Main ↔ plugin host Ext, communicating via RPC)
- Services as classes with DI, avoid exported functions (functions can't be overridden)
ContributionProviderinstead of@multiInjectfor collecting multiple implementations- Use
bindRootContributionProvider(notbindContributionProvider) when binding contribution providers in top-level modules.bindContributionProviderretains a reference to whichever child container first resolves it, causing memory leaks. Only usebindContributionProviderwhen contributions are intentionally scoped to a child container (e.g. connection-scoped containers viaConnectionContainerModule). - URI strings for cross-platform file paths, never raw paths
- Localize user-facing strings with
nls.localize()ornls.localizeByDefault()
Testing:
- Unit tests:
*.spec.ts - UI tests:
*.ui-spec.ts - Slow tests:
*.slow-spec.ts - Test resources go in
test-resources/directory
- Node.js ≥20
- TypeScript ~5.9.3 with strict settings (target ES2023, module CommonJS)
- React 18.2.0 for UI components
- Monaco Editor for code editing
Key Technologies:
- Express.js for backend HTTP server
- InversifyJS for dependency injection
- Lerna for monorepo management
- Webpack for application bundling
- Lumino 2.x for widget system (tabs, panels, dock layout)
Key Config Files:
configs/base.tsconfig.json- TypeScript base config (all packages extend this)configs/base.eslintrc.json- ESLint parser/base rulesconfigs/build.eslintrc.json- ESLint build rules (packages extend this)configs/mocharc.yml- Mocha test runner configconfigs/nyc.json- Test coverage config