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

vbapm

v0.11.0

Published

A package manager and build tool for VBA

Readme

vbapm

A package manager and build tool for VBA. It lets you version-control your VBA source code and Excel XML structure as plain text files, then build them back into a working workbook or add-in.

Support for other MS Office file format is planed.

Installation

Option 1: Install as a global npm package

Prerequisites: Node.js v22 or higher.

npm install -g vbapm

Option 2: Standalone installer

Windows

In powershell, run the following:

iwr https://raw.githubusercontent.com/vbapm/installer/refs/heads/main/install.ps1 | iex

Mac

In terminal, run the following:

curl -fsSL https://raw.githubusercontent.com/vbapm/installer/refs/heads/main/install.sh | sh

For more recent versions of Office for Mac, you will need to trust access to the VBA project object model for vbapm to work correctly:

If you run into any issues during installation, please see the known issues for the installer or create a new issue with details about what's happening.

:rocket: You're ready to go! Open a new command-line session (cmd / terminal) and try vba --help

macOS users: You must enable "Trust access to the VBA project object model" in Excel → Preferences → Security for vbapm to work.

Getting Started

Initialize a project from an existing workbook

If you already have an .xlsm or .xlam file and want to start managing it with vbapm, use the --from flag:

vba new my-project --from "C:\path\to\existing.xlsm"

If you want to start in the current directory instead of creating a new one:

vba init

This creates a new directory my-project/ containing:

my-project/
├── vbaproject.toml          # Project manifest (name, target type, sources)
├── src/                     # Extracted VBA source files
│   ├── Module1.bas          #   Standard modules → .bas
│   ├── Sheet1.cls           #   Document modules → .cls
│   ├── ThisWorkbook.cls     #   Workbook module → .cls
│   ├── MyClass.cls          #   Class modules → .cls
│   └── UserForm1.frm        #   UserForms → .frm
├── target/                  # Extracted Excel XML structure
│   ├── [Content_Types].xml
│   └── xl/
│       ├── workbook.xml
│       ├── worksheets/
│       ├── styles.xml
│       └── ...
├── build/                   # Built workbook (customizable via build-dir in vbaproject.toml)
│   └── my-project.xlsm
└── .git/, .gitignore, ...   # Git version control (by default)

vbapm automates two extraction steps:

  1. VBA source code — The vbapm add-in runs a macro that extracts every VBComponent from the workbook as its native file type, along with a project.json listing non-built-in references.

  2. XML structure — The workbook is unzipped and its internal XML (sheets, styles, ribbon, etc.) is extracted to target/.

Use vba init --from existing.xlsm instead of vba new if you want to initialize in the current directory rather than creating a new one.

The basic workflow

Once your project is initialized, the day-to-day loop is:

  1. Edit your source files in src/ and register new ones with vba add
  2. Build the workbook with vba build
  3. Test your macros in Excel, or use vba run from the command line
  4. Extract changes back to source with vba extract, then git diff and commit

Putting it together

# Start with a new project from an existing file
vba new expense-tracker --from C:\workbooks\budget-2025.xlsm
cd expense-tracker

# Make edits to src/ files, then rebuild
vba build --open

# After tweaking in Excel, capture the changes
vba extract

# Commit your work
git add . && git commit -m "Add expense tracker project"

Programmatic Usage

You can also use vbapm as a library (e.g. from a VS Code extension):

const { buildProject, loadProject, env } = require("vbapm");

// Override working directory
env.cwd = "/path/to/project";

const project = await loadProject();
await buildProject(project);

Usage

new

Create a new folder with a blank/generated vbapm project inside

Create a folder "project-name" with a blank xlsm project:

vba new project-name.xlsm

(equivalent to above)

vba new project-name --target xlsm

Create a folder "from-existing" with a project from an existing workbook:

vba new from-existing --from existing.xlsm

Create a blank package for sharing as a library between projects:

vba new json-converter --package

By default, vba new initializes a git repository and creates .gitignore, .gitattributes and .editorconfig template files. Use --no-git to skip git init and --no-conf to skip the template files:

vba new project-name.xlsm --no-git
vba new project-name.xlsm --no-conf

init

Create a blank/generated vbapm project in the current folder

Create a blank xlsm project with the current folder's name:

vba init --target xlsm

Create a project from an existing workbook:

vba init --from existing.xlsm

Create a blank package:

vba init --package

By default, vba init initializes a git repository and creates .gitignore, .gitattributes and .editorconfig template files. Use --no-git to skip git init and --no-conf to skip the template files:

vba init --target xlsm --no-git
vba init --target xlsm --no-conf

add

Create a new source file in src/ and register it in vbaproject.toml.

Create a standard module:

vba add Module1

Create a class module:

vba add JsonParser --type class

Register an existing source file by path (no overwrite):

vba add .\src\Test.bas

Create and register a file in a nested path (missing folders are created):

vba add .\src\features\auth\LoginHelper.bas

Add a development-only source to [dev-src]:

vba add TestHelpers --dev

build

Build an Excel workbook from the project's source. The built file is written to the directory specified by build-dir in vbaproject.toml (defaults to build/). If a previously built file is found it is moved to a .backup directory under the build directory to protect against losing any previously saved work.

To output the built file in the project root instead, set build-dir = "." in vbaproject.toml.

Build a project:

vba build

Build and open a project for editing:

vba build --open

Build a package using a blank target:

vba build --target xlsm

Build a project, excluding any development src, dependencies, or references:

vba build --release

Update only the VBA source in an existing built target, without rebuilding the workbook:

vba build --vba-only

This is equivalent to vba update and requires the target to already exist in the build directory.

extract

Once you've completed your edits and are ready to commit your changes, extract your project with vba extract.

Extract a project:

vba extract

Extract a previously-built package:

vba extract --target xlsm

Only extract the XML files (skip VBA source extraction):

vba extract --xml-only

Only extract the VBA source (skip XML extraction):

vba extract --vba-only

Note: vba export is deprecated and will be removed in a future version. Use vba extract instead.

update

vba update writes the current VBA source directly into an existing built target file (including one currently open in Excel), without going through a full build cycle.

Update VBA source in the built target:

vba update

Update VBA in a specific target type:

vba update --target xlsm

Update excluding dev-src, dev-dependencies, and dev-references:

vba update --release

Update and leave the target open in Excel after updating:

vba update --open

open

vba open opens the current built target file in Excel. This is a convenience shortcut when you want to start editing a target you've already built.

Open the built target:

vba open

Open a specific target type:

vba open --target xlsm

close

vba close closes the built target file that is currently open in Excel. By default changes are discarded — pass --save to keep them.

Close the built target (discard unsaved changes):

vba close

Close and save changes:

vba close --save

Close a specific target type:

vba close --target xlsm

run

vba run opens the built workbook in Excel, runs a public VBA function, captures its return value and prints it to stdout.

Syntax

vba run <Module.Function> [<arg1> <arg2> ...] [--file PATH] [--target TYPE]

VBA code conventions

The vba run command calls a public function (not a Sub). The function:

  • Must be Public
  • Must return a String (or a type that VBA can coerce to a string)
  • Can accept up to 10 arguments (passed positionally from the CLI)
  • Arguments arrive as Variant on the VBA side
' (Module: Messages.bas)
Public Function SayHi(Name As Variant) As String
  SayHi = "Howdy " & Name & "!"
End Function
vba run Messages.SayHi Tim
Howdy Tim!

Passing arguments

Arguments are passed positionally and arrive as Variant in VBA:

# Single argument
vba run ExcelManipulator.WriteCell "Sheet1,5,3,Hello World"

# Multiple arguments
vba run Math.Add 3 7

# Quoted strings with spaces
vba run Messages.SayHi "Tim Hall"

Return value

If the VBA function returns a string, it is printed to stdout. This is how you receive data back from Excel.

If the function returns a JSON string matching {"success": true/false, "messages": [...], "errors": [...]} it is parsed as a structured result.

Targeting options

Run against the built project (default — run from project directory):

vba run ExcelManipulator.AddSheet "MyNewSheet"

Run against a specific file:

vba run Messages.SayHi Tim --file "C:\path\to\workbook.xlsm"

Run against a specific target type (if project has multiple targets):

vba run MyModule.MyFunction arg1 --target xlsm

Practical workflow for Excel manipulation

vba add ExcelManipulator
# → edit src/ExcelManipulator.bas with your VBA functions
vba build
vba run ExcelManipulator.AddSheet "Report"
vba run ExcelManipulator.WriteCell "Report,1,1,Title"
vba run ExcelManipulator.GetCell "Report,1,1"
# → output: "Title"

Example VBA module for cell manipulation:

Public Function AddSheet(SheetName As Variant) As String
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets.Add
    ws.Name = CStr(SheetName)
    AddSheet = "Created sheet: " & SheetName
End Function

Public Function WriteCell(Args As Variant) As String
    Dim parts() As String
    parts = Split(CStr(Args), ",")
    ThisWorkbook.Sheets(parts(0)).Cells(CLng(parts(1)), CLng(parts(2))).Value = parts(3)
    WriteCell = "Wrote '" & parts(3) & "' to " & parts(0) & "!" & parts(1) & "," & parts(2)
End Function

Public Function GetCell(Args As Variant) As String
    Dim parts() As String
    parts = Split(CStr(Args), ",")
    GetCell = CStr(ThisWorkbook.Sheets(parts(0)).Cells(CLng(parts(1)), CLng(parts(2))).Value)
End Function

For complex operations, consider encoding data as JSON strings in arguments and parsing with a VBA JSON library.

Tips

  • Build before running — vba run targets the built workbook in build/, not the source files.
  • Extract after manipulation — Changes made by macros (new sheets, cell values) live in the built workbook. Run vba extract to persist them back to source.
  • Close before rebuilding — If a workbook is open in Excel, close it with vba close --save before running vba build again.
  • Run from the project root — Commands expect vbaproject.toml in the current directory, unless you use --file to target a specific workbook.
  • Quote arguments with spaces — Shell escaping is handled by vbapm, but wrap arguments containing spaces in quotes.

Manifest (vbaproject.toml)

The vbapm manifest (vbaproject.toml) serves as the foundation for your project and provides information on your package, source, dependencies, references, and targets, as detailed below.

[project] or [package]

The [package] / [project] section includes general information about your package. You should choose [package] if your project is only intended to be used as a utility inside another project and [project] if your project is a standalone tool.

Here are the main properties:

  • name (required): The name of the project/package
  • version (required for [package]): version number following semver.
  • authors (required for [package]): name(s) of the author(s)
  • target (required for [project]): define what application/extension to use when building your project.
  • codename custom VBA project name shown in the VBE (defaults to "VBAProject")

Example 1

[project]
name = "awesome-excel-project"
target = "xlsm"
codename = "AwesomeProject"

Example 2

[package]
name = "awesome-vba-package"
authors = ["Me <[email protected]>"]
version = "0.1.0"

version

vbapm follows Semantic Versioning. Make sure you adopt a compatible versioning approach if you intend to publish to the repository.

target

target is used to define what application/extension to use when building your project. It can be a string for the extension, in which case target/ includes the source files for creating the target. Otherwise, type and path can be used to define a custom target path.

Example 1:

target = "xlsm"
# equivalent to target = { type = "xlsm", path = "target" }

Example 2:

target = { type = "xlam", path = "targets/xlam" }

codename

codename sets the VBA project name displayed in the Visual Basic Editor (VBE). When omitted, the default "VBAProject" is used. Only valid for [project] — ignored for [package].

codename = "MyCustomProject"

[build-dir]

build-dir specifies where the built .xlsm / .xlam file is written. Defaults to "build" when omitted. Set to "." to output in the project root.

build-dir = "."    # output to project root instead of build/

[source.files]

Will contain the list of source code files to be included in the VBA-Enabled Document at build time.

Two listing modes are supported:

Individual entries — each file is listed explicitly by name (name = "path" or { path }):

[source.files]
A = "src/A.bas"
B = "src/B.cls"
C = { path = "src/C.bas" }

Use this mode with vba init --list-all or vba init --from <workbook> for full control over which files are tracked.

Wildcard entries — groups of files are discovered by glob pattern (the default for vba init):

[source.files]
Objects = "src/Excel Objects/**/*.cls"
Forms = "src/Forms/**/*.frm"
Modules = "src/Modules/**/*.bas"
Classes = "src/Class Modules/**/*.cls"

Wildcards are expanded at build time — any file on disk matching the glob is picked up automatically, so you don't need to run vba add after creating a new module. When you delete a module in the VBE and run vba extract, the corresponding file on disk is removed automatically.

Note: Wildcard and individual entries can coexist. Components already covered by an existing wildcard pattern do not get duplicate individual entries added which means the wildcard handles discovery on the next build or extract.

Legacy [src]: Older manifests use a top-level [src] section. It is still accepted for backwards compatibility, but is deprecated — entries are treated as [source.files] and a warning is shown until the manifest is updated.

[source]

Optional settings that control how source files are organised and written to disk.

[source]
# folder = "src"            # Implicit overridable default. 
subfolders = { Modules = "Modules", Forms = "Forms", Classes = "Classes" }

| Key | Default | Description | |---|---|---| | folder | "src" | Base directory for all source files (relative to project root). New components extracted from a workbook are placed under this directory. | | subfolders | — | Organises source files into type-based subdirectories: .bas → Modules/, .frm → Forms/, .cls → Classes/. Objects maps document modules (ThisWorkbook, sheets) to a subfolder. | | encoding | — | Default encoding for source files (e.g. "cp1252", "utf-8"). Can be overridden per-source in [source.files]. | | sort | — | Enforce ordering in the [source.files] section. Options: by-types (group by component type), alphabetical (sort by name). | | include-empty-objects | true | When false, blank document objects (ThisWorkbook, sheet modules) are skipped during export. |

[dependencies]

The dependencies section lists the packages that your project depends on. Each dependency can be specified in one of three ways:

name = "version" or

  • version
  • path
  • git (and branch, tag, or rev)
[dependencies]
a = "1" # Equivalent to ^1
b = "=2.0.0" # Precisely 2.0.0
c = { version = "3" }

d = { path = "./packages/d" }

e = { git = "https://..." } # master
f = { git = "https://...", branch = "dev" }
g = { git = "https://", tag = "bugfix" }
h = { git = "https://", rev = "abc1234" }

[references]

  • version ("MAJOR.MINOR")
  • guid ("{...}")
[references]
Scripting = { version = "1.0", guid = "{...}" }

[dev-src,dependencies,references]

[dev-src], [dev-dependencies], and [dev-references] are included during development and are excluded when building with the --release flag (i.e. vba build --release)

Development

Prerequisites

  1. git clone this repo
  2. Install Node.js v22 or later
    • Note: For CLI builds, Node v23+ only supports Windows x64 (win-x64). 32-bit Windows (win-x86) is no longer available upstream.
  3. Install node-gyp dependencies for Mac or Windows

Build

  1. Run pnpm install
  2. Run pnpm run format
  3. Run pnpm run build:cli It will build the CLI/library in lib, plus ensured vendor node runtime is available.
  4. Run pnpm run build:addins It will build the Excel addin that performs workbook/VBA operations from inside Office.

Test

  1. Run pnpm test It will run unit tests
  2. Run pnpm run test:e2e It will run the end-to-end CLI scenarios in excel.e2e.ts, covering workflows like build, extract, new, and version against fixtures. To keep temporary e2e work folders for manual inspection, set KEEP_E2E_TMP=1 before running (PowerShell: $env:KEEP_E2E_TMP=1; pnpm run test:e2e, cmd: set KEEP_E2E_TMP=1 && pnpm run test:e2e). To echo each e2e command output even on successful runs, use --verbose (PowerShell: pnpm run test:e2e:background -- --verbose) or set E2E_VERBOSE=1 (PowerShell: $env:E2E_VERBOSE=1; pnpm run test:e2e:background).

Install local version

To install the local version you can use the devinstall.ps1 script available in the installer submodule.

# Update submodule
git submodule update --init --recursive installer

# Run devinstall
.\installer\devinstall.ps1

Release

  1. Run pnpm version
  2. Run pnpm run release

Acknowledgments

This project is a fork of the original vba-blocks project by Tim Hall.