How it works
Snapds is built around a small, fast pipeline that runs entirely inside the extension host — no runtime dependencies are added to your project.
Resolution
To read a package’s types, Snapds first has to find it on disk. It uses a
multi-strategy resolver so it works in flat installs, nested node_modules, and
monorepos alike:
Upward node_modules walk
Starting at the workspace root, it checks {dir}/node_modules/{pkg} and walks up
parent directories to the filesystem root.
require.resolve
Falls back to Node’s resolver (require.resolve('{pkg}', { paths: [root] })).
Monorepo deep search
Searches across the workspace for hoisted or nested deps.
Local workspace package
Finally scans every package.json (excluding node_modules) and matches by
name, so locally-built workspace packages resolve too.
Local component sources. In-repo design systems (shadcn or your own folder)
skip node_modules entirely: Snapds reads the folder’s source .tsx directly,
resolves the folder from a components.json alias (or a folder you pick) via
tsconfig paths, and injects each component from its per-file path alias
(e.g. @/components/ui/button) rather than a package name. Their cache is keyed
by a content signature (file mtimes), and a file watcher re-indexes on edit.
In a monorepo, every sub-app’s components.json is detected independently,
so the same @/* alias resolves to each app’s own folder — sources stay
distinct (keyed by their workspace-relative folder) and never collide.
Monorepo version resolution
In monorepos where the same package is installed at different versions across different apps, Snapds resolves the right version for the file you’re editing rather than always using the hoisted root copy.
On startup Snapds discovers every installation of each registered package across
the workspace (via findFiles) and indexes them by version, sorted
highest-semver-first.
When the active editor changes, Snapds walks up from the focused file’s directory
to find the nearest node_modules/{pkg} directory. That installation — and its
cached component schema — is shown in the Component Properties panel.
If no installation is found along the file’s path (e.g. a shared config file at the repo root), Snapds falls back to the highest semver found in the workspace. The props panel header shows an auto badge when the version was inferred this way, and a version dropdown when more than one installation exists so you can pin a specific version manually.
When the package is not listed in the focused file’s nearest package.json, an
Add to this app button appears. Clicking it writes the dependency into that
package.json (under dependencies) and shows a reminder to run pnpm install.
Introspection
Once resolved, Snapds parses the package’s typings entry with
react-docgen-typescript
plus the TypeScript compiler API. For each component it extracts:
- Props — name, type (enum, string, boolean, number, function, ReactNode), the raw TS type, whether it’s required, the default value, and the JSDoc description.
- Enum values — so the props panel can render selects instead of free text.
- Polymorphic components — a compiler pass enumerates exported value
components that docgen alone misses (e.g.
as-style components). forwardRefcomponents — for components typed asForwardRefExoticComponent<Omit<Props, "ref">>(common in libraries like Radix UI), a compiler pass reads thePropsinterface directly. Props a component inherits from sibling packages are kept — only standard DOM/React attributes, and props declared in interfaces JSDoc-tagged@private/@internal, are filtered out as noise.
Components are filtered to those whose display name starts with an uppercase
letter and are not tagged @internal or listed in your config’s ignore.
Caching
Introspection is cached in the extension’s global state under a key of the form:
ds.cache.v{schemaVersion}.{name}@{version}[@{configMtimeMs}]Key points:
versionis read live fromnode_modules/{pkg}/package.jsonat cache time, not from your saved settings. This means runningpnpm updateinvalidates the cache automatically — no manual cache clear needed.- Each installed version has its own cache entry. In a monorepo with
v1.2.0in one app andv2.1.0in another, both entries coexist. Switching the version selector in the props panel hits a warm cache immediately after startup. configMtimeMsis appended when asnapds.config.jsonor.snapds.jsonexists. Editing it invalidates all entries for that workspace.- User overrides are stored separately and applied after the cache, so tweaking them never forces a re-parse.
Re-opening the gallery is instant; the cache only rebuilds when one of those inputs actually changes.
Code generation
Dropping a component produces a bare-specifier import and JSX:
import { Button } from '@acme/ui';New imports are merged into an existing statement for the same package when one exists — including multi-line, Prettier-formatted imports — and otherwise inserted after the last import in the file, on its own line.
Webviews
The gallery, props panel, and settings are native VS Code webviews, so the UI matches your editor theme and stays fast even on large monorepos thanks to differential updates.