Documentation

One engine, three surfaces. Every command, tool, and setting below calls the same analysis in @tinyport/core. There is no surface-specific behaviour hiding anywhere.

Getting started

Install

CLI

No install needed. npx runs the latest each time.

npx tinyport report "src/**/*.ts"

VS Code

Install TinyPort from the Marketplace, or from Quick Open.

ext install vantaso.tinyport-vscode

MCP server

Add it to your MCP client config. It caches lookups under ~/.tinyport/cache and never writes to your project unless you call apply_fixes with dry_run: false.

mcp client config
{
  "mcpServers": {
    "tinyport": { "command": "npx", "args": ["-y", "@tinyport/mcp"] }
  }
}
Commands

CLI reference

tinyport check

Fail CI when a file goes over its byte budget. Leads with a summary, then details the files that matter. Exit 1 the moment one is over.

FlagEffect
--budget <bytes>Per-file budget in bytes.
--bundleMeasure real bundled + gzipped size (slower).
--allDetail every file with imports, not just the heaviest.
--jsonMachine-readable output.
terminal
$ tinyport check src --budget 100000
89 of 142 files · 47 packages · 3.2 MB
Over budget
src/app.tsx 212 kB, over by 112 kB
framer-motion 114 kB (bundled)
moment 72 kB (bundled)
3 of 142 files over budget

tinyport report

Rank the heaviest imported packages across the project, and how many files each touches.

FlagEffect
--top <n>How many packages to list (default 20).
--bundleMeasure real bundled + gzipped size (slower).
--jsonMachine-readable output.
terminal
$ tinyport report "src/**"
react 170 kB (shipped) 1 file
framer-motion 114 kB (bundled) 12 files
moment 72 kB (bundled) 5 files
next 49 kB (bundled) 5 files

tinyport fix

Sort and group imports. Prints a diff by default and writes nothing until you pass --write.

FlagEffect
--writeApply the changes (otherwise a diff is printed).
--remove-unusedAlso remove imports flagged unused, before sorting.
terminal
$ tinyport fix src/app.tsx
src/app.tsx
- import moment from "moment"
- import { motion } from "framer-motion"
+ import { motion } from "framer-motion"
+ import moment from "moment"
1 file would change (run with --write)
For AI coding agents

MCP tools

Six tools over stdio, each calling the same @tinyport/core analysis the CLI and extension use. No separate agent-specific logic to drift out of sync.

ToolInputWhat it does
analyze_file{ path, bundle? }One file's imports: sizes, unused bindings, duplicates, lighter alternatives, budget verdict. Returns fix ids for apply_fixes.
scan_project{ glob?, top?, bundle? }Rank the heaviest imported packages across the project.
check_budget{ paths?, budget }Check files against a per-file budget. Call it after an edit to confirm the change stayed within size.
suggest_alternatives{ package }A lighter replacement for a package, with an honest compatibility rating.
explain_package{ package, bundle? }A package's version and its installed, shipped, bundled, and gzipped size, each labelled distinctly.
apply_fixes{ path, fixIds, dry_run? }Apply named fixes. Dry-run unless dry_run: false. Only fix ids from analyze_file are accepted.
apply_fixes: the dry-run contract

dry_run defaults to true. Writing needs an explicit dry_run: false; there is no way to write by omission.

Only fix ids analyze_file returned for that exact file are accepted. A stale or invented id is refused with the list of valid ids, never silently ignored.

tinyport-vscode

VS Code settings

Everything lives under tinyport.*. alt+shift+o (editor focus) runs Sort Imports.

SettingDefaultWhat it does
tinyport.showInlineSizetrueInline size decoration at the end of each import line.
tinyport.showGutterBarstrueGutter bar sized relative to the file's heaviest import.
tinyport.greyOutUnusedtrueDims imports TinyPort detects as unused.
tinyport.bundledfalseMeasure real bundled + gzipped size via esbuild instead of the disk estimate. Slower; off the inline-decoration hot path regardless.
tinyport.sortBy"size"Order within a group: size, length, alpha, or natural.
tinyport.sortDirectionunsetasc or desc. Unset follows sortBy: desc for size, asc otherwise.
tinyport.groups["builtin","external",…]Ordered import groups; a blank line separates each group present in a file.
tinyport.budget.filenullPer-file size budget in bytes. null disables it.
tinyport.budget.packagenullPer-package size budget in bytes. null disables it.
tinyport.printWidthnullLine width when planning a rewrite. null infers from Prettier/EditorConfig, otherwise 100.
tinyport.alternatives{}Extra package to lighter-alternative entries, merged over the built-in table.
tinyport.sortOnSavefalseSort imports on save. Checked fresh each save for a competing organiser (see below).
tinyport.removeUnusedOnSavefalseExperimental. Also remove unused imports on save, independent of sortOnSave.

Sort-on-save never fights your setup

Before it acts, sortOnSave checks for another organiser already doing the job: VS Code's source.organizeImports, an ESLint sort rule, or a Prettier import-sort plugin. If it finds one, it names the tool and does not sort, re-checked on every save. To run alongside another organiser, add "source.organizeImports.tinyport": true to editor.codeActionsOnSave.

How a number is computed

Sizing

shipped · default

The installed size on disk of the package a specifier resolves to. Instant, offline. Not what a bundler ships.

bundled · --bundle

The real entry, bundled and minified with esbuild and gzipped. The actual bytes a browser receives. Slower, so it is opt-in.

Every figure is labelled

A number is always tagged (bundled), (shipped), or (type-only), so an estimate is never mistaken for a measurement. Bundling falls back to (shipped) per package whenever esbuild is unavailable or an entry can't be bundled.

Type-only is zero

A whole-statement import type contributes exactly 0 bytes and is labelled type-only, never the package's disk figure. It compiles away entirely.

Cached per version

Bundled and gzip figures are cached per package, version, and subpath. A published version's bundle never changes, so a cached success is reused rather than redone.