TinyPort for VS Code
Live import weight in the editor, the same analysis as the tinyport CLI and the
@tinyport/mcp server, rendered where you're editing, not run on demand.
This extension holds no analysis logic of its own: it supplies VS Code's filesystem
and an esbuild bundler to @tinyport/core's ports, and renders the Report core
hands back. If a decision about an import's size, order, or findings shows up in this
package instead of @tinyport/core, that's a bug.
Install
Install TinyPort from the
VS Code Marketplace,
or from Quick Open (Cmd/Ctrl+P):
ext install vantaso.tinyport-vscode
Open any JavaScript or TypeScript file and every import grows a size tag. No
configuration required.
What it does
- Inline size at the end of every import line,
import { motion } from "framer-motion" gets a 114 kB tag, import { clsx } from "clsx" gets 600 B.
Coloured green under a per-package budget, amber approaching it, vermilion over,
muted when no budget is set. A whole-statement import type and an unresolved
relative import show nothing; there's nothing true to print for either.
- Gutter bars sized relative to the heaviest import in the file, so the worst
offender is visible without reading a single number.
- Hover over any import for its resolved size (bundled, shipped, or type-only),
and a lighter alternative when TinyPort's built-in table knows one (e.g.
moment →
date-fns/dayjs, lodash → per-function imports).
- Diagnostics in the Problems panel for unused imports (dimmed in the editor too)
and a file that's over its budget.
- Six commands (Command Palette: search "TinyPort") and matching sort/remove
code actions, all built on the exact same planners the CLI's
fix/report
commands use, never a second implementation:
TinyPort: Sort Imports, sorts and groups, one undoable edit.
TinyPort: Sort and Remove Unused, sorts and drops unused imports, one
undoable edit.
TinyPort: Show Import Report, a full per-file breakdown in an output channel,
plus a one-line total.
TinyPort: Scan Project for Heavy Imports, ranks every imported package across
the workspace, cancellable.
TinyPort: Set Budget for this Workspace, sets a file or package byte budget.
TinyPort: Toggle Accurate (Bundled) Sizing, flips tinyport.bundled.
- Status bar item with the active file's total (
190 kB / 100 kB when a file
budget is set, vermilion background when over); click it to run the file report.
- Sort-on-save, off by default, that refuses to turn itself on when it detects a
competing import organiser already doing the job, see below.
Sizes work out of the box
Every surface above works the moment you install this extension, no configuration,
no esbuild of your own required. The default is a disk-based estimate
(shipped): the installed size of the package(s) a specifier resolves to, instant
and offline. Turn on tinyport.bundled for the real, measured number, the
actual minified + gzipped bytes a bundler would ship for that specific import (e.g.
next/link is 47.5 kB installed but 15.2 kB gzipped once actually bundled), TinyPort
prefers the analysed project's own esbuild and falls back to a copy of its own when
the project has none, so accurate sizing works out of the box too. Real bundling is
slower and runs off the hot path: typing never blocks on it, and inline decorations
keep using the instant disk estimate even with tinyport.bundled on, only a hover or
an explicit report picks up the measured number once it resolves. Every figure that
leans on the disk estimate is marked (est), so an estimate is never mistaken for a
measurement.
Settings
All under tinyport.*:
| Setting |
Default |
What it does |
tinyport.showInlineSize |
true |
Inline size decoration at the end of each import line. |
tinyport.showGutterBars |
true |
Gutter bar sized relative to the file's heaviest import. |
tinyport.greyOutUnused |
true |
Dims imports TinyPort detects as unused. |
tinyport.bundled |
false |
Measure 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.sortDirection |
(unset) |
asc or desc. Unset follows sortBy, desc for size, asc otherwise. |
tinyport.groups |
["builtin", "external", "internal", "parent", "sibling"] |
Ordered import groups; a blank line separates each group present in a file. |
tinyport.budget.file |
null |
Per-file size budget in bytes. null disables it. |
tinyport.budget.package |
null |
Per-package size budget in bytes. null disables it. |
tinyport.printWidth |
null |
Line width when planning a rewrite. null infers from Prettier/EditorConfig when available, otherwise 100. |
tinyport.alternatives |
{} |
Extra package -> lighter-alternative entries, merged over TinyPort's built-in table. |
tinyport.sortOnSave |
false |
Sort imports automatically on save. See below. |
tinyport.removeUnusedOnSave |
false |
Experimental. Also remove unused imports on save, independent of sortOnSave. Stays experimental and off until the removal path has had an independent re-review. |
A keybinding (alt+shift+o, editor focus) runs tinyport.sortImports.
Sort-on-save and the conflict problem
tinyport.sortOnSave is off by default, and turning it on doesn't unconditionally
make TinyPort sort every save: before it acts, it checks the workspace for another
tool already doing the same job,
editor.codeActionsOnSave's source.organizeImports (either shape VS Code
accepts, the per-kind object or the flat array),
- an ESLint rule (
import/order, simple-import-sort/imports,
perfectionist/sort-imports) enabled in the nearest config,
- a Prettier import-sort plugin (
@ianvs/prettier-plugin-sort-imports,
@trivago/prettier-plugin-sort-imports).
If it finds one, it names the tool in a warning notification and does not sort,
checked fresh on every single save, not just once when the setting is turned on, so a
conflict introduced later (a new ESLint rule, a teammate's Prettier config change)
still gets caught on the very next save rather than silently fighting the other tool.
Want TinyPort specifically, alongside another organiser? Add
"source.organizeImports.tinyport": true to editor.codeActionsOnSave for explicit
sequencing, TinyPort's own code-action kind never trips its own conflict check.
Removal is never a save side effect of sortOnSave: tinyport.removeUnusedOnSave is
a separate, independent, experimental setting, turning one on does not turn on the
other.
Running it from source
To hack on the extension (rather than install the Marketplace build), run it in
the Extension Development Host:
From the repo root, build every package once (the extension depends on
@tinyport/core and @tinyport/node-support's built output):
pnpm install
pnpm -r build
Open the repo root in VS Code and press F5 (or Run ▸ Start Debugging), using
the Run TinyPort Extension launch configuration
(.vscode/launch.json at the repo root). This runs the extension's own build
(.vscode/tasks.json's tinyport-extension: build task, pnpm --filter tinyport-vscode build) as a preLaunchTask, then opens a second
Extension Development Host window with this extension loaded.
No launch config handy? Run the build yourself and start the host by hand:
pnpm --filter tinyport-vscode build
code --extensionDevelopmentPath="$(pwd)/packages/extension"
In the Extension Development Host window, open a .js/.ts/.jsx/.tsx file
with a few node_modules imports (this activates the extension, see
activationEvents in package.json). Each import line should grow a size tag at
its end and a bar in the gutter within ~300 ms; the status bar (bottom right)
should show the file's total.
Re-run step 2 (or just the build command) after changing extension source, the dev
host doesn't hot-reload; use Developer: Reload Window inside it, or stop and
re-launch, to pick up a rebuilt dist/extension.js.
Development
pnpm vitest run packages/extension # pure builders/adapters, no editor needed
pnpm --filter tinyport-vscode test:integration # @vscode/test-electron, needs a real download+launch
pnpm typecheck
node ../../scripts/check-versions.mjs # this package's version must track the workspace
pnpm --filter tinyport-vscode build # produces dist/extension.js, a single bundled CJS file
pnpm --filter tinyport-vscode package # produces the .vsix, see scripts/package-vsix.mjs
config.ts, analysis/engine.ts (the only module that calls into @tinyport/core),
analysis/debounce.ts, actions/offsets.ts, and every ui/*/actions/*/save/*
module except the ones ending Manager.ts/Controller.ts/onSave.ts are pure and
unit-tested with vitest, no VS Code runtime needed. A static import "vscode"
anywhere in a module's graph makes it unloadable outside the extension host, so the
colour thresholds, the (est) suffix, the sort/remove plans, and the sort-on-save
conflict detector are all provable in vitest; only the thin wiring that turns those
specs into real vscode.* API calls needs an editor, and that thin layer is what
test-integration/ (@vscode/test-electron) exists to cover, activation, a command
mutating and undoing a real buffer, sortOnSave actually gating a real save, and
actions/offsets.ts's offset-to-position conversion checked against VS Code's own
document.positionAt across CRLF, lone-\r, and multi-UTF-16-unit text.
Status
Every feature in What it does is built and tested. Not in v1,
deliberately:
- Registry metadata in the hover (version, deprecated, outdated), has a slot, waits
on a later tier.
- "Switch to alternative" as an action, hover names an alternative; rewriting call
sites for you needs preview discipline this version doesn't have yet.
removeUnusedOnSave graduating out of experimental.
- A web extension host, desktop only for now.
MIT © 2026 Muhammad Ali