SmartPack v2.1.1
Compile, bundle and minify SCSS, CSS and JavaScript the moment you save.
SmartPack watches your web project and rebuilds only what changed: it compiles SCSS,
bundles and minifies CSS/JS, and resolves the JavaScript import graph — automatically,
on every save. Configuration lives in a single smartpack.json at your project root.
Highlights in 2.0
SmartPack was rewritten from the ground up:
- Out-of-process extension — runs outside Visual Studio's process; a hang or crash
in the build engine can no longer touch the IDE.
- Fast — a long-lived build engine stays warm between requests; a typical rebuild
takes milliseconds.
- Real dependency tracking —
@use/@import partials and JS imports are learned
from the compiler itself and persisted across sessions. Edit a partial no module
declares: the right bundle still rebuilds.
- Navigable errors — build errors land in the Error List bound to the real source
file and line; progress goes to the "Smart Pack" output channel.
- Safe writes — outputs are written atomically and left untouched when content did
not change; every read/write path is validated against project boundaries.
- Chains — one module's output can be another module's source
(
main.scss → main.bundle.css → main.min.css); ordering is automatic, cycles are rejected.
- Self-contained engine — the Node.js build pipeline (Rollup, sass, PostCSS, terser)
ships inside the extension package. No downloads at runtime.
New in 2.1 — Remote Aliases (URL) 🌐
An alias path may now be an https URL: import a library published elsewhere without
copying it into your project. Rollup applies tree-shaking, so only the exports you
actually use end up in the bundle.
{
"alias": [ { "key": "@wui_helpers", "path": "https://wui.karakok.net/v3.2.0/dist/js/helpers.js" } ],
"modules": [ { "source": [ "/js/app.js" ], "output": "/js/app.min.js" } ]
}
import { formatDatetime } from '@wui_helpers'; // only formatDatetime enters the bundle
The URL never reaches the bundler: SmartPack downloads the file once into
.smartpack/remote/ inside the project (committed by default — builds stay offline-capable
and reproducible) and hands the build the local copy. No network request per build;
refreshing happens at session start via ETag, or on demand with right-click
smartpack.json → Update Remote Aliases. A jsconfig.json entry is maintained
automatically so IntelliSense resolves the alias too. The target file must be published as
ESM and side-effect free. Add a remote alias from the smartpack.json right-click menu
(Add Alias Path — type the URL into the path field) or by editing the file directly.
Requirements
- Visual Studio 2022 (17.14+) or Visual Studio 2026
- Node.js 22.11+ on
PATH — https://nodejs.org (LTS is fine).
Verify with node --version.
Quick Start
- Install the extension and open your web project.
- Right-click a
.scss, .css or .js file in Solution Explorer → pick a command
from the Smart Pack submenu.
smartpack.json is created automatically (the root snaps to wwwroot when present),
the module compiles immediately and watching starts. Every save refreshes the output.
How to Use
📂 Tools Menu — under Tools > Smart Pack: Go Documents (this page),
Restart Engine (rebuilds the engine and watchers from scratch), Open Engine Folder,
Migrate from [Bundle and Minifier] / Migrate from [Web Compiler] /
Migrate from [SmartPack v1] (imports legacy configurations) and
Change Status as Temp (temporarily disables/enables automatic builds).
📄 File context menu — commands appear based on the selected file type:
- CSS: Minify CSS · Bundle CSS Files · Bundle and Minify · Exec Rel. Packs · Exclude This File
- SCSS: Resolve SCSS · Resolve and Minify · Exec Rel. Packs · Exclude This File
- JS: Minify JS · Resolve JS · Resolve and Minify · Bundle JS Files · Bundle and Minify ·
Exec Rel. Packs · Exclude This File
Exclude This File adds the selected file to the unwatch list, so saving it no longer
triggers a rebuild. Exec Rel. Packs runs only the modules the selected file participates in.
🧰 Toolbar — a "Smart Pack" toolbar (enable it under View > Toolbars) offers
Go Documents, Change Status as Temp and Open Engine Folder as one-click buttons.
🎛️ Settings — in Visual Studio's settings editor under Smart Pack > JS Settings:
jsOutputType (iife/cjs/esm, default iife) — the output format the JS resolve
commands pre-select when creating a module.
⚙️ smartpack.json context menu — Exec All Packs (runs every module),
Add Alias Path (browse for a folder — or type an https URL to add a remote alias),
Add Unwatch Path (browse dialog writing a root-relative path),
Update Remote Aliases (re-downloads the cached copies of remote aliases).
Note: If a file is already used in a module's source, its command stays visible;
executing it shows a "this file is already part of a pack" message with an option to
open the configuration.
Commands and the Configuration They Produce
Commands add the module to smartpack.json and compile immediately; the watcher takes
over on subsequent saves. Only non-default fields are written to the file.
🚀 Minify CSS — minifies the selected CSS file (suggested output: name.min.css).
{ "source": [ "/css/site.css" ], "output": "/css/site.min.css" }
🚀 Bundle CSS Files / Bundle and Minify (CSS) — combines the selected CSS files
(optionally minified). The bundle dialog lists files root-relative; reorder with ▲▼.
{ "source": [ "/css/a.css", "/css/b.css" ], "output": "/css/bundle.min.css" }
🚀 Resolve SCSS / Resolve and Minify (SCSS) — compiles the SCSS file;
@use/@import partials are resolved and watched automatically. The indented .sass
syntax is also accepted as a source in smartpack.json. The minify variant also writes
emitPlaceholder: true so the output nests in Solution Explorer (see the field below).
{ "source": [ "/scss/site.scss" ], "output": "/css/site.min.css", "emitPlaceholder": true }
🚀 Minify JS — minifies the file as-is, no import resolution.
{ "source": [ "/js/app.js" ], "output": "/js/app.min.js", "resolve": false }
🚀 Resolve JS / Resolve and Minify (JS) — resolves the import graph and bundles into
a single file (suggested output: name.bundle.js / name.min.js). The output format is
not asked in a dialog; the jsOutputType setting (iife/cjs/esm, default iife)
is used.
{ "source": [ "/js/app.js" ], "output": "/js/app.min.js" }
🚀 Bundle JS Files / Bundle and Minify (JS) — concatenates the selected JS files
without resolving; check Wrap in IIFE in the dialog to wrap the output.
{ "source": [ "/js/a.js", "/js/b.js" ], "output": "/js/bundle.min.js", "resolve": false, "wrapIife": true }
🚀 Exec All Packs / Exec Rel. Packs — runs every module in smartpack.json, or
only the modules related to the selected file.
🔗 Building chains — an output can feed another module; SmartPack orders the chain
itself and rejects cycles:
{ "modules": [
{ "source": [ "/js/app.min.js", "/js/counter.min.js" ], "output": "/js/bundle.min.js", "resolve": false },
{ "source": [ "/js/app.js" ], "output": "/js/app.min.js" },
{ "source": [ "/js/counter.js" ], "output": "/js/counter.min.js" }
] }
Configuration Reference
All paths are relative to root — no exceptions.
Root fields
| Field |
Type |
Default |
Description |
root |
string |
project dir |
The root all paths are relative to (e.g. /wwwroot) |
modules |
object[] |
— |
The work list (module fields below) |
alias |
object[] |
— |
{ "key": "@core", "path": "/js/core" } — only meaningful for resolved JS: import util from "@core/util.js" then resolves to /js/core/util.js. path may also be an https URL (see Remote Aliases above). Duplicate keys, or keys that are prefixes of each other, make the configuration invalid |
unwatch |
string[] |
— |
Paths the watcher ignores |
Module fields
| Field |
Type |
Default |
Description |
source |
string[] |
— |
Source file(s) |
output |
string |
— |
Output file |
minify |
bool |
true |
Minify the output |
resolve |
bool |
true |
JS only: resolve the import graph; false = plain concatenation |
outputFormat |
string |
iife |
JS with resolve: true only: iife / cjs / esm |
wrapIife |
bool |
false |
Wrap concatenated output in an IIFE |
emitUnminified |
bool |
false |
With minify: true, also write a readable copy next to the output (site.min.css → site.css). Ignored when minify is false |
emitPlaceholder |
bool |
false |
Write a placeholder file (a single explanatory comment) at the unminified name (site.min.css → site.css) so Visual Studio nests the .min output under it in Solution Explorer. Ignored (with a warning) when emitUnminified already writes the real copy, when the output name carries no .min suffix, or when the placeholder path would collide with a module source. Written automatically by Resolve and Minify (SCSS) |
showSpParams |
bool |
false |
Prepend a one-line /*! SmartPack ... */ info banner to the output. Change detection ignores the banner, so its timestamp means "last real change", not "last run" |
Migrating from Bundler & Minifier / Web Compiler
Tools > Smart Pack > Migrate scans your solution and imports bundleconfig.json and
compilerconfig.json configurations into smartpack.json. Entries that cannot be
migrated are reported with their reason; your legacy files are never deleted.
Upgrading from SmartPack v1
Uninstall v1 first, restart Visual Studio, then install v2 — the two versions use
different extension stores and can otherwise run side by side with duplicated menus.
Your v1 smartpack.json files can be converted automatically:
Tools > Smart Pack > Migrate from [SmartPack v1] scans the solution, rewrites each config
to the v2 contract and keeps a smartpack.json.v1.bak backup next to it. The conversion
preserves behavior; anything that needs your decision is reported in the Output window.
What changed in the configuration — v2 tells you exactly what to fix instead of guessing:
doNotResolve → resolve (positive, default true). A config still carrying the
old field reports an error and that project is not built until fixed:
"doNotResolve": true becomes "resolve": false.
umd and system output formats were removed — remaining: iife (default),
cjs, esm.
outputFormat only applies when resolve: true — for plain concatenation use
"wrapIife": true if you need IIFE wrapping.
- Module-level
alias was removed — aliases live at the root level only; module-level
entries are ignored with a warning.
allowOutsideRoot was removed — v2 writes only under root; reading is broader
(node_modules outside the root is fine), writing is strict.
- Unminified sass copies are now opt-in — v1 silently wrote an unminified
.css
next to minified sass output; in v2 add "emitUnminified": true to keep that file
updated (otherwise it stays on disk but never refreshes).
- Computed fields (
inType, multiSources, fullJson) are ignored and removed on
the next config write.
- Output bytes differ from v1 (modern sass API, current Rollup/terser). If you commit
generated files, expect a one-time diff — switch the whole team at once.
Where SmartPack Keeps Its Files
- Build engine: inside the extension's install folder under
engine/
(Tools > Smart Pack > Open Engine Folder).
%LOCALAPPDATA%\SmartPack\log — diagnostic log (engine lifecycle, errors, migration
report; pruned automatically at 14 days / 10 MB).
%LOCALAPPDATA%\SmartPack\graph — the learned dependency graph (persisted across sessions).
%LOCALAPPDATA%\SmartPack\disabled.txt — the temporary-disable marker.
Upgrading from v1: the old C:\SmartApps\SmartPack directory is no longer used and can
be deleted safely.
Troubleshooting
- Installation ends with "encountered a problem": update Visual Studio and retry —
older 17.14 servicing releases have a known issue in the extension registration step
on Turkish-locale systems (fixed in VS 2026).
- Installation runs to the end, then fails with return code
-2146232800: an earlier
install attempt was interrupted (Visual Studio left open, install canceled, or a crash)
and left an orphaned folder behind; the installer refuses to overwrite it. Open
%TEMP%\dd_setup_*_errors.log — if it says
...\Common7\IDE\VSExtensions\<random-name>\.vsextension\extension.json' already exists,
close all Visual Studio windows, delete that <random-name> folder from an elevated
prompt, and run the installer again.
- Node.js missing: SmartPack tells you in the "Smart Pack" output channel; install
Node, add it to
PATH, restart Visual Studio.
- Where do errors go? Build errors: Error List (double-click jumps to the source).
Progress and warnings: the "Smart Pack" output channel.
- Remote alias serves stale content after the publisher updated it? Right-click
smartpack.json → Update Remote Aliases — it re-downloads unconditionally,
bypassing the ETag check.
- Engine stuck?
Tools > Smart Pack > Restart Engine.
Powered by KNs