npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-docinfo

v0.7.0

Published

static analysis for TypeScript and Svelte

Readme

svelte-docinfo

static analysis for TypeScript and Svelte 📜 svelte-docinfo.fuz.dev

svelte-docinfo extracts JSON describing the exports of TypeScript and Svelte modules for open-ended use cases like docs, code search, and dev tools. It uses the TypeScript compiler API and svelte2tsx to resolve types, track exports+imports, and extract semantic details. It includes a Vite plugin, CLI, and programmatic API in the npm package.

npm i -D svelte-docinfo

svelte-docinfo is largely inspired by sveld, but instead of AST-only inspection it uses the TypeScript compiler API for richer information, and also analyzes TypeScript modules. The docs compare them.

The library is mostly complete for Svelte 5 and used in production websites, but you may find gaps and flaws -- please open issues for bugs and discussions for everything else!

Dependencies are minimal and the tool's scope is limited to data, not presentation. The docs website for svelte-docinfo is made using itself and fuz_ui components.

AI disclosure: the code and docs beyond the intro were mostly written by Claude Code with uneven human guidance. The first release took 5 months of intermittent work and ~500 manual commits to extract its initial implementation from fuz_ui, which was more limited, lacking the fancy TS compiler usage, and grew slowly over years without AI assistance.

Quick start

Given a project with a couple of files in src/lib/:

// src/lib/math.ts

/**
 * Add two numbers.
 * @param a - first number
 * @param b - second number
 * @returns the sum
 */
export const add = (a: number, b: number): number => a + b;
<!-- src/lib/Calculator.svelte -->

<!--
	@component
	Calculator component for demonstrating Svelte analysis.
-->
<script lang="ts">
	let {
		result = $bindable(0),
		mode = 'add',
		disabled = false
	}: {
		/** Current result (bindable). */
		result?: number;
		/** Operation mode. */
		mode?: 'add' | 'multiply';
		/** Disable the calculator. */
		disabled?: boolean;
	} = $props();
</script>

Three ways to integrate svelte-docinfo:

  1. Vite plugin - recommended for SvelteKit and Vite projects. Serves a virtual module with HMR in dev mode:
// vite.config.ts
import svelteDocinfo from 'svelte-docinfo/vite.js';

export default defineConfig({ plugins: [sveltekit(), svelteDocinfo()] });
// then anywhere in your app:
// import {modules} from 'virtual:svelte-docinfo';
  1. CLI - quick analysis from the command line, useful for ad-hoc inspection and shell pipelines:
npx svelte-docinfo --pretty
  1. API - programmatic access for build tools, custom pipelines, and standalone scripts:
import { analyzeFromFiles } from 'svelte-docinfo';

const { modules } = await analyzeFromFiles({ projectRoot: process.cwd() });

All three produce the same JSON shape:

{
	"modules": [
		{
			"path": "Calculator.svelte",
			"declarations": [
				{
					"name": "Calculator",
					"kind": "component",
					"docComment": "Calculator component for demonstrating Svelte analysis.",
					"sourceLine": 5,
					"props": [
						{
							"name": "result",
							"type": "number",
							"optional": true,
							"description": "Current result (bindable).",
							"defaultValue": "0",
							"bindable": true
						},
						{
							"name": "mode",
							"type": "\"add\" | \"multiply\"",
							"typeInfo": {
								"kind": "union",
								"members": [
									{ "kind": "literal", "value": "add", "text": "\"add\"" },
									{ "kind": "literal", "value": "multiply", "text": "\"multiply\"" }
								]
							},
							"optional": true,
							"description": "Operation mode.",
							"defaultValue": "'add'"
						},
						{
							"name": "disabled",
							"type": "boolean",
							"optional": true,
							"description": "Disable the calculator.",
							"defaultValue": "false"
						}
					]
				}
			]
		},
		{
			"path": "math.ts",
			"declarations": [
				{
					"name": "add",
					"kind": "function",
					"docComment": "Add two numbers.",
					"typeSignature": "(a: number, b: number): number",
					"sourceLine": 7,
					"parameters": [
						{ "name": "a", "type": "number", "description": "first number" },
						{ "name": "b", "type": "number", "description": "second number" }
					],
					"returnType": "number",
					"returnDescription": "the sum"
				}
			]
		}
	]
}

The extraction demo has more examples, and see the docs for the complete output format. You may also want to browse the test fixtures.

Vite plugin

  1. Add the plugin to vite.config.ts:
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import svelteDocinfo from 'svelte-docinfo/vite.js';

export default defineConfig({
	plugins: [sveltekit(), svelteDocinfo()]
});
  1. Add TypeScript support in app.d.ts:
/// <reference types="svelte-docinfo/virtual-svelte-docinfo.js" />
  1. Import the virtual module anywhere in your app:
import { modules, diagnostics } from 'virtual:svelte-docinfo';
// or: import data from 'virtual:svelte-docinfo';

Plugin options:

| Option | Default | Description | | --------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | projectRoot | Vite's resolved config.root | Absolute path to project root. | | include | — | Glob patterns to include (relative to projectRoot; absolute inside it accepted). Collapses discovery: 'auto' to glob; combining with discovery: 'exports' throws. Widens the source scope — see the notes below. | | exclude | ['**/*.test.ts', '**/*.spec.ts', '**/internal/**'] | Glob patterns to exclude. An array fully replaces the defaults; the callback form extends them ((defaults) => [...defaults, '**/*.gen.ts']). The always-on baseline below still applies. | | discovery | 'auto' | Discovery strategy: 'auto' | 'exports' | 'glob'. 'exports' is strict and fails if package.json exports is missing or empty. | | distDir | 'dist' | Dist directory name for exports-based discovery. | | sourceOptions | {sourcePaths: ['src/lib'], …} | Partial overrides for default source options (SvelteKit src/lib layout). | | resolveDependencies | true | Resolve import dependencies. When false, dependencies/dependents stay empty. | | onDuplicates | — | Dispatch on duplicate declaration names: 'throw' | 'warn' | callback. Diagnostic emits regardless of dispatch. | | hmrDebounceMs | 100 | HMR debounce delay in milliseconds. |

Source-scope notes (shared with the CLI and analyzeFromFiles):

  • Explicit include patterns widen the source scope: each pattern's static base joins sourceOptions.sourcePaths, so include-discovered files emit modules (with paths relative to the widened set's common root) and the dev-mode watcher tracks them. A pattern with no base ('**/*.ts', a literal root file) scopes the whole project root as source and logs an info line naming it.
  • An always-on baseline applies beneath any exclude: node_modules and dot-directories below a matched source path are never source. It's matched relative to the matched source path — an explicit dot-directory source path (sourcePaths: ['.hidden/src']) still works — and is independent of exclude overrides. dist/build/coverage are ordinary names and stay in.
  • The src/lib/internal/ convention: internal/ directories are excluded by default (the **/internal/** default pattern) — internal modules ship in the package for public modules to import but aren't documented. Packages typically pair the directory with an "./internal/*": null package.json exports entry blocking consumer imports; exports-based discovery honors null-target keys with Node's best-match resolution semantics, so blocked subpaths are never discovered. Internal modules stay live in dev: the analysis session owns them as watched context files, so editing an internal type re-analyzes the public modules that use it. A public re-export of an internal symbol (export { x } from './internal/helper.js') documents at the re-export site as a full synthesized declaration. Re-include internal modules wholesale with the exclude callback: exclude: (defaults) => defaults.filter((p) => p !== '**/internal/**').
  • Paths and patterns resolve against projectRoot: absolute entries inside the root are accepted (stored root-relative), and anything resolving outside the root throws at options creation instead of silently emitting nothing — a root-anchored '/src/lib' is filesystem-absolute, not shorthand for 'src/lib'.

The options interface is VitePluginSvelteDocinfoOptions from svelte-docinfo/vite.js (not re-exported from the main barrel). See the docs site and examples/vite/ for more.

If TypeScript reports Cannot find module 'virtual:svelte-docinfo', ensure the /// <reference> line is in your app.d.ts.

CLI

npx svelte-docinfo                    # analyze cwd, print JSON to stdout
npx svelte-docinfo -o output.json     # write to a file instead
npx svelte-docinfo ./packages/my-lib  # analyze a specific directory
npx svelte-docinfo --pretty           # pretty-print the JSON output

Output is compact JSON by default. Use --pretty for readable output, or pipe through jq for queries:

npx svelte-docinfo | jq '.modules | length'                  # count modules
npx svelte-docinfo | jq -r '.modules[].declarations[].name'  # list all exported names

Info messages print to stderr; only JSON goes to stdout. The terminal interleaves them visually, but > and | capture clean JSON. Use -q/--quiet to suppress info on stderr (warnings and errors still print).

All CLI options:

| Flag | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [project-root] | Project root directory (default: cwd) | | -i, --include <pattern> | Include pattern (repeatable, replaces exports discovery, widens the source scope; incompatible with --discovery exports) | | -e, --exclude <pattern> | Exclude glob, applied at discovery and analysis (repeatable; fully replaces the defaults **/*.test.ts, **/*.spec.ts, **/internal/** — no merge; the always-on baseline applies beneath) | | -o, --output <file> | Output file (default: stdout; pass - for explicit stdout, so -o "$OUT" works when $OUT=-) | | --discovery <mode> | auto | exports | glob (default: auto — exports first, glob fallback). exports is strict and fails if package.json exports is missing | | --dist-dir <dir> | Dist directory for exports discovery (default: dist) | | --source-dir <dir> | Source directory, relative to project root or absolute inside it (default: src/lib). Repeatable for monorepos; drives the implicit include glob when no --include is provided | | --source-root <dir> | Source root for module-path stripping (default: single source-dir or longest common prefix; pass . for project-relative paths) | | --on-duplicates <mode> | Dispatch on duplicate declaration names: throw | warn (default: emit duplicate_declaration diagnostic, no dispatch) | | --only <pattern> | Glob filter applied to module paths in output (repeatable). Full project is still analyzed (re-exports/dependents stay correct); diagnostics aren't filtered | | --no-resolve-dependencies | Disable dependency resolution | | --pretty | Pretty-print JSON output (default: compact) | | -q, --quiet | Suppress info messages on stderr (warnings and errors still print) | | -V, --version | Show version number |

The Vite plugin's source-scope notes apply here too: explicit --include patterns widen the source scope (module paths become relative to the widened root, and a pattern with no base scopes the whole project root and logs an info line); node_modules and dot-directories below a source dir are always excluded regardless of --exclude; absolute paths and patterns inside the project root are accepted, and out-of-root ones fail loudly.

Exit codes: 0 (success), 1 (analysis errors), 2 (CLI errors).

See examples/cli/ for more usage patterns.

API

Three entry points, same AnalyzeResultJson shape ({modules, diagnostics}):

| Function | Use when | | ----------------------- | ------------------------------------------------------------------------------------------------- | | analyzeFromFiles | One-shot. Standalone projects — handles file discovery and dependency resolution | | analyze | One-shot. Build-tool integration — you supply SourceFileInfo[] already in memory | | createAnalysisSession | Incremental. Long-lived consumers (Vite plugin, LSP-style tools) reusing parsed ASTs across calls |

For most projects, analyzeFromFiles is all you need. It reads your package.json exports to discover source files, falling back to glob patterns:

import { analyzeFromFiles } from 'svelte-docinfo';

const { modules } = await analyzeFromFiles({ projectRoot: process.cwd() });

Pass include for custom patterns or discovery: 'glob' to always use glob. Use discovery: 'exports' for strict mode that throws if package.json has no usable exports field. When you already have file contents in memory, use analyze instead of analyzeFromFiles to skip file discovery. For just the discovery step (without running analysis), use discoverSourceFiles (also in the main svelte-docinfo import).

For long-lived consumers (a Vite plugin reacting to file edits, an LSP-style tool), createAnalysisSession returns a persistent handle that reuses parsed ASTs across calls — see examples/api/analyze-session.js.

See examples/api/ for more patterns including custom discovery, diagnostics, sessions, and error handling.

Import from svelte-docinfo for the common API, or from subpaths like svelte-docinfo/typescript-exports.js for lower-level access. See the API docs for the full reference.

Features

  • Full type resolution: infers complex types without manual annotations, including generics, imported types, and inferred return types
  • Structured types: an optional typeInfo tree (TypeJson) beside the flat type strings on props, parameters, return types (returnTypeInfo, overloads included), type members, and variable/type declarations — union and intersection members (alias names kept, enum members as {value, text} pairs), reference type arguments (named generic instantiations like Snippet<[a: string]> classify as references), array and tuple elements (with readonly markers), so a union alias documents more than its own name — and absent wherever the flat string already says everything
  • TSDoc/JSDoc parsing: the common doc tags (@param, @returns, @throws, @example, @deprecated, @internal, @see, @since, @default) plus @nodocs, @mutates, and @module for file-level comments; divergent spellings parse as synonyms (@return, TSDoc's @defaultValue). @internal is a marker (internalMessage, prose kept), not an exclusion — that's @nodocs
  • Alias-lost name recovery: schema-inferred aliases (type Foo = z.infer<typeof S> — any indexed-access or conditional right-hand side) lose their name in the checker, which expands their structure at every use; typeInfo recovers the name as {kind: 'reference', name} from the written annotation where one exists, and — via an identity-keyed registry of the analyzed set's exported lost aliases — at unannotated inferred positions too, null-bearing optionals included; registry-recovered references also carry module (the declaring module's ModuleJson.path, always an emitted module) for collision-exact linking. Losses nothing recovers surface as alias_lost warnings (readable degradations like z.enum literal unions and .brand() intersections excluded)
  • Merged value+type symbols: export const Foo = z.strictObject({...}) + export type Foo = z.infer<typeof Foo> (one symbol, both spaces) documents the type meaning — structure and members like the un-merged equivalent — with mergedValue: true marking the name as also importable as a runtime value; JSDoc falls back to the const's docs, and generateImport emits a value import
  • Svelte 5 component props: extracts prop types, descriptions, defaults, and bindability via svelte2tsx
  • Svelte 5 snippets: kind: 'snippet' for template snippets (with structured parameters), acceptsChildren on components
  • Svelte 5 reactivity runes: detects $state, $state.raw, $derived, $derived.by on variables and class fields (reactivity field) — syntactic detection, surfaces wherever the rune pattern appears
  • Class members: public + protected fields, methods, constructors, getters/setters, generics; private (private and #field) excluded
  • Enums: regular and const, with member values and per-member JSDoc
  • Function overloads: all public overload signatures with per-overload JSDoc
  • Dependency graphs: tracks imports between modules and computes dependents
  • Re-export tracking: alsoExportedFrom arrays on canonical declarations plus the forward view ModuleJson.reExports (so barrels are self-describing), aliasOf for renames, starExports for export * from './x', and externalReExports/externalStarExports for direct re-exports from packages — with resolveExportSurface() to combine them all into a module's full export surface using ES star semantics
  • Namespace re-exports: export * as ns from './x' synthesized as kind: 'namespace'
  • Source locations: file and line for every declaration
  • Build-tool agnostic: works with any source: file system, build pipeline, or in-memory
  • Diagnostic collection: accumulates warnings and errors without halting; partial: true flags incomplete declarations

Known gaps:

  • context tracking (transitive detection of common patterns seems tractable)
  • standalone namespace Foo {} declarations and decorators are not yet supported
  • Svelte 4 legacy features (slots, events, $restProps) are out of scope — Svelte 5 snippets and callback props replace most of them

Issues for bugs and discussions are welcome!

Documentation

Credits

sveld (by Eric Liu, @metonym) was this project's main inspiration.

Extracted from fuz_ui, which has example components using the data for docs websites like DeclarationDetail.

svelte-docinfo is more featureful than it would be otherwise thanks to LLM assistance, mostly Claude Code.

Built on TypeScript and svelte2tsx, see package.json for full dependencies.

Contributing

fuz.dev/contributing

License

MIT