Skip to content

Bundle the extension with esbuild for faster activation and web compatibility #19

Description

The extension is published as a collection of raw JavaScript files (src/extension.js, src/remoteUrl.js) plus a node_modules tree at runtime. The VS Code bundling guide explicitly recommends bundling because:

Loading 100 small files is much slower than loading one large file.

More importantly, only bundled extensions can run in VS Code for the Web (github.dev, vscode.dev) — when VS Code runs in the browser, it loads exactly one file per extension.

Request

Current experience

  • The published VSIX contains the source tree as-is, plus any runtime dependencies under node_modules
  • Activation is slower than necessary because VS Code must resolve and load each file individually
  • The extension cannot run in VS Code for the Web because there is no single bundled entry point

Desired experience

  • A single bundled dist/extension.js is produced at build time
  • Activation is faster because only one file is loaded
  • The same bundle is reused as the foundation for a future web-extension build (tracked in a separate issue)
  • Source files (src/), build configs, and node_modules are excluded from the published VSIX

Acceptance criteria

  • The extension is bundled into dist/extension.js using a configured bundler
  • package.json main points to the bundled output (./dist/extension.js)
  • A vscode:prepublish script exists and runs the production bundle build
  • Source maps are generated (hidden source maps for production, full for development)
  • node_modules, src/, and bundler config files are excluded from the VSIX via .vscodeignore
  • The bundled VSIX is meaningfully smaller than the current published artifact (size to be measured)
  • Existing functionality (Explorer context menu, custom view, keybindings) is unchanged

Technical decisions

Bundler choice: esbuild over webpack.

Criterion esbuild webpack
Speed Significantly faster Slower
Configuration complexity Minimal Verbose
Recommended by VS Code docs ✅ Listed first ✅ Also supported
Tree-shaking / minification ✅ Built-in ✅ Built-in
TypeScript support Native (strips types) Requires ts-loader

The codebase is plain JavaScript with JSDoc, so esbuild's minimal configuration is sufficient. Webpack would add complexity without proportional value.

Build script: Use a small esbuild.js build script (per the VS Code bundling guide example) that supports --production and --watch flags.

Entry point: src/extension.js → bundled to dist/extension.js. The vscode module must be marked external (it is provided by the runtime, not bundled).

Platform target: Start with platform: 'node' to preserve current behavior. A follow-up issue will introduce a webworker target for web-extension support.

Source maps: sourcemap: !production. Production builds use hidden source maps (--devtool hidden-source-map equivalent in esbuild) so stack traces are decodable but the .js.map is not advertised in the source.

package.json scripts:

{
  "scripts": {
    "lint": "eslint src",
    "compile": "node esbuild.js",
    "watch": "node esbuild.js --watch",
    "package": "node esbuild.js --production",
    "vscode:prepublish": "npm run package",
    "package:vsix": "vsce package",
    "publish:vsix": "vsce publish"
  }
}

The existing package / publish scripts are renamed to package:vsix / publish:vsix to avoid colliding with the bundling package script. The CI workflow currently calls vsce package directly; verify it still works after this rename or invoke npm run package:vsix.

Updated .vscodeignore:

.vscode/**
.vscode-test/**
.github/**
node_modules/**
src/**
.gitignore
.eslintrc*
eslint.config.js
esbuild.js
package-lock.json
*.vsix
tsconfig*.json

Important

Adding src/** removes the source files from the VSIX. This is intentional — the bundled dist/extension.js is now the sole runtime artifact. The resources/ folder (icons used by package.json contributions) must stay included.

Coordination with web extension support: A separate issue will add a browser entry point and a webworker-targeted build. This issue establishes the bundling foundation; web-extension support builds on it.

Coordination with build workflow: The CI workflow (.github/workflows/build-vscode-extension.yml) calls npx vsce package after npm ci. With vscode:prepublish defined, vsce will run the bundle automatically — no workflow changes are strictly required, but the VSIX verification step (#4) should be updated to expect dist/extension.js instead of src/extension.js.


Implementation plan

Build configuration

  • Add esbuild to devDependencies
  • Add esbuild.js build script supporting --production and --watch flags, with vscode marked external
  • Update package.json main to ./dist/extension.js
  • Update package.json scripts to add compile, watch, package, vscode:prepublish and rename existing package/publish to package:vsix/publish:vsix

Project hygiene

CI

Validation

  • Run npm run package locally and confirm dist/extension.js is produced and minified
  • Run vsce package and inspect the VSIX contents — src/ and node_modules/ must be absent
  • Install the VSIX locally and verify all commands, the tree view, and keybindings work
  • Compare the VSIX size before/after and record the reduction in the PR description

Documentation

  • Update README.md Development section with the new npm run watch workflow

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions