Skip to content

Project layout

A freshly scaffolded project:

my-extension/
├── src/
│ ├── extension/
│ │ ├── extension.ts # bootstrap(registry) — wires VS Code on activate
│ │ └── _registry.ts # AUTO-GENERATED by `bun run gen`
│ ├── panels/<name>.ts # one file = one webview panel (definePanel)
│ ├── commands/<name>.ts # one file = one palette command (defineCommand)
│ ├── menus/<name>.ts # activity-bar container + items (defineMenu)
│ ├── treeViews/<name>.ts # data-driven tree view (defineTreeView)
│ ├── subpanels/<name>.ts # inline webview section inside a menu
│ ├── statusBars/<name>.ts # status bar item (defineStatusBar)
│ ├── webview/
│ │ ├── panels/<name>/ # React UI per panel (App.tsx, main.tsx)
│ │ └── components/ # shared themed components (after `components add`)
│ ├── services/ # business logic (e.g. <Model>Service.ts)
│ ├── models/ # typed entities (after `model add`)
│ ├── helpers/ # db.ts, secrets.ts, … (after `db init` / `helper add`)
│ └── shared/
│ ├── api.ts # RPC contracts (interface per panel)
│ └── vsceasy/ # framework runtime — synced via `vsceasy upgrade`
├── scripts/gen.ts # registry + contributes generator
├── .vscode/launch.json # Extension Development Host launch
├── vite.config.ts # webview build
└── package.json # esbuild for extension, vite for UI

Owned vs generated

  • You own everything under panels/, commands/, webview/, services/, models/, helpers/, and shared/api.ts. Edit freely.
  • Generatedsrc/extension/_registry.ts and package.json#contributes are rewritten by gen. Don’t hand-edit them.
  • Framework runtimesrc/shared/vsceasy/* and scripts/gen.ts are owned by vsceasy. Keep them current with vsceasy upgrade; don’t edit them.

Build pipeline

  • Extension code → esbuilddist/extension.js (CJS, node target).
  • Each panel/subpanel UI → vitedist/webview/<kind>/<name>/.
  • bun run dev runs both in watch; F5 launches the dev host.
  • bun run package.vsix via @vscode/vsce.

When do I need to run gen?

gen rewrites two things: src/extension/_registry.ts (what panels / commands / menus / jobs / etc. exist) and package.json#contributes (how they’re declared to VS Code). So:

Run gen when a hand edit changes what exists or how it’s contributed:

  • Add, delete, or rename a file in src/panels/, src/commands/, src/menus/, src/statusBars/, src/subpanels/, src/treeViews/, or src/jobs/.
  • Change a panel/command/menu’s id, title, command, menu, icon, keybinding, or when.

You don’t need gen when a hand edit only touches logic:

  • The body of an rpc, run, or getChildren handler.
  • A webview App.tsx (vite recompiles that, not gen).
  • A model, service, store, or helper — those aren’t in the registry.

The vsceasy generators run gen for you. And bun run dev, build, and launch all run it first — so if you use those, you rarely run gen by hand.