vsce package and
.vsix DistributionThe vsce (Visual Studio Code Extension) command-line
tool is the official packaging and publishing tool for VSCode
extensions. It generates .vsix files that serve as
VSCode-specific distribution format - comparable to JAR files in the
Java world, but with extension-specific metadata and dependencies.
Installation is done globally via npm:
npm install -g @vscode/vsceThe package.json defines all packaging-relevant
metadata. Mandatory fields for extension packaging:
{
"name": "my-typescript-extension",
"displayName": "My TypeScript Extension",
"description": "Professional TypeScript extension for enterprise development",
"version": "1.0.0",
"publisher": "company-name",
"engines": {
"vscode": "^1.74.0"
},
"categories": [
"Programming Languages",
"Linters",
"Formatters"
],
"keywords": [
"typescript",
"enterprise",
"productivity"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "extension.format",
"title": "Format TypeScript Code"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"package": "vsce package",
"deploy": "vsce publish"
},
"devDependencies": {
"@vscode/vsce": "^2.15.0"
}
}For enterprise extensions, additional metadata is required:
{
"repository": {
"type": "git",
"url": "https://github.com/company/vscode-extension.git"
},
"bugs": {
"url": "https://github.com/company/vscode-extension/issues"
},
"homepage": "https://github.com/company/vscode-extension#readme",
"license": "MIT",
"icon": "resources/icon.png",
"galleryBanner": {
"color": "#1e1e1e",
"theme": "dark"
},
"qna": "marketplace",
"extensionDependencies": [
"ms-vscode.vscode-typescript-next"
],
"extensionPack": [
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode"
]
}The .vsixignore file defines exclusions for packaging -
analogous to .gitignore:
# Source files
src/**
*.ts
!out/**/*.js
# Development files
.vscode/**
.gitignore
.gitattributes
tsconfig.json
tslint.json
.eslintrc.json
# Test files
test/**
**/*.test.js
**/*.test.ts
# Documentation
*.md
!README.md
!CHANGELOG.md
# Build artifacts
node_modules/**
.nyc_output/**
coverage/**
# OS files
.DS_Store
Thumbs.db
# IDE files
*.suo
*.user
*.sln
*.cache
A .vsix file is a ZIP archive with defined
structure:
extension.vsix
├── [Content_Types].xml # MIME-Type definitions
├── extension.vsixmanifest # Extension manifest
├── extension/
│ ├── package.json # Extension metadata
│ ├── out/
│ │ ├── extension.js # Compiled code
│ │ └── **/*.js
│ ├── resources/
│ │ ├── icon.png # Extension icon
│ │ └── **/*
│ └── README.md # Documentation
└── _rels/
└── .rels # Relationship definitions
The extension.vsixmanifest contains VSCode-specific
metadata:
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0"
xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011">
<Metadata>
<Identity Language="en-US"
Id="my-typescript-extension"
Version="1.0.0"
Publisher="company-name"/>
<DisplayName>My TypeScript Extension</DisplayName>
<Description xml:space="preserve">Professional TypeScript extension</Description>
<Categories>Programming Languages,Linters,Formatters</Categories>
<Tags>typescript,enterprise,productivity</Tags>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"
Version="[1.74.0,)"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest"
Path="extension/package.json"
Addressable="true"/>
</Assets>
</PackageManifest>The basic packaging workflow:
# 1. Install dependencies
npm install
# 2. Compile TypeScript
npm run compile
# 3. Run tests (optional)
npm test
# 4. Create VSIX
vsce package
# 5. With specific version
vsce package --out ./dist/extension-1.0.0.vsix
# 6. Pre-release version
vsce package --pre-releaseAutomated build process via npm scripts:
{
"scripts": {
"clean": "rimraf out dist *.vsix",
"compile": "tsc -p ./",
"compile:watch": "tsc -watch -p ./",
"lint": "eslint src --ext ts",
"test": "npm run compile && node ./out/test/runTest.js",
"prepackage": "npm run clean && npm run compile && npm run test",
"package": "vsce package --out ./dist/",
"package:beta": "vsce package --pre-release --out ./dist/",
"version:patch": "npm version patch && npm run package",
"version:minor": "npm version minor && npm run package",
"version:major": "npm version major && npm run package"
}
}VSIX files can be installed locally without marketplace publication:
# Installation via command line
code --install-extension path/to/extension.vsix
# Installation via VSCode UI
# Command Palette (Ctrl+Shift+P) -> "Extensions: Install from VSIX..."Programmatic installation via Extension API:
// src/installer.ts
import * as vscode from 'vscode';
import * as path from 'path';
export class ExtensionInstaller {
public static async installVsix(vsixPath: string): Promise<void> {
try {
const uri = vscode.Uri.file(path.resolve(vsixPath));
await vscode.commands.executeCommand('workbench.extensions.installExtension', uri);
vscode.window.showInformationMessage('Extension installed successfully');
} catch (error) {
vscode.window.showErrorMessage(`Installation failed: ${error}`);
throw error;
}
}
public static async uninstallExtension(extensionId: string): Promise<void> {
try {
await vscode.commands.executeCommand('workbench.extensions.uninstallExtension', extensionId);
vscode.window.showInformationMessage('Extension uninstalled successfully');
} catch (error) {
vscode.window.showErrorMessage(`Uninstallation failed: ${error}`);
throw error;
}
}
}Semantic versioning for extensions:
{
"version": "1.2.3",
"engines": {
"vscode": "^1.74.0"
},
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/vscode": "^1.74.0",
"typescript": "^4.9.4"
},
"extensionDependencies": [
"ms-vscode.vscode-typescript-next@>=4.9.0"
]
}Automatic versioning with Git integration:
# Version bumping with automatic Git tag
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.1 -> 1.1.0
npm version major # 1.1.0 -> 2.0.0
# Custom version
npm version 1.2.3-beta.1For enterprise environments without marketplace access:
// src/enterpriseDistribution.ts
export interface DistributionConfig {
artifactRepository: string;
version: string;
channel: 'stable' | 'beta' | 'alpha';
}
export class EnterpriseDistributor {
constructor(private config: DistributionConfig) {}
public async uploadToArtifactory(vsixPath: string): Promise<void> {
const artifactUrl = `${this.config.artifactRepository}/vscode-extensions/${this.config.channel}`;
// Artifact upload implementation
// Integration with Nexus, Artifactory, or internal repository
}
public async generateInstallScript(): Promise<string> {
return `
#!/bin/bash
EXTENSION_URL="${this.config.artifactRepository}/extension-${this.config.version}.vsix"
TEMP_FILE="/tmp/extension.vsix"
# Download extension
curl -o "$TEMP_FILE" "$EXTENSION_URL"
# Install extension
code --install-extension "$TEMP_FILE"
# Cleanup
rm "$TEMP_FILE"
echo "Extension ${this.config.version} installed successfully"
`;
}
}GitHub Actions workflow for automatic packaging:
# .github/workflows/package.yml
name: Package Extension
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
package:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Package extension
run: |
npm run package
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: vsix-package
path: '*.vsix'
- name: Create release
if: startsWith(github.ref, 'refs/tags/')
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
body: |
Extension package for version ${{ github.ref }}
draft: false
prerelease: falseAutomated validation of VSIX packages:
// src/validation.ts
import * as fs from 'fs';
import * as path from 'path';
import { execSync } from 'child_process';
export class VsixValidator {
public static async validatePackage(vsixPath: string): Promise<ValidationResult> {
const result: ValidationResult = {
isValid: true,
errors: [],
warnings: []
};
// 1. File existence check
if (!fs.existsSync(vsixPath)) {
result.errors.push(`VSIX file not found: ${vsixPath}`);
result.isValid = false;
return result;
}
// 2. File size check (max 50MB)
const stats = fs.statSync(vsixPath);
const maxSize = 50 * 1024 * 1024; // 50MB
if (stats.size > maxSize) {
result.warnings.push(`Large package size: ${stats.size} bytes`);
}
// 3. Package structure validation
try {
const extractPath = path.join(__dirname, 'temp-extract');
execSync(`unzip -q "${vsixPath}" -d "${extractPath}"`);
// Check required files
const requiredFiles = [
'extension.vsixmanifest',
'[Content_Types].xml',
'extension/package.json'
];
for (const file of requiredFiles) {
const filePath = path.join(extractPath, file);
if (!fs.existsExists(filePath)) {
result.errors.push(`Missing required file: ${file}`);
result.isValid = false;
}
}
// Cleanup
execSync(`rm -rf "${extractPath}"`);
} catch (error) {
result.errors.push(`Package extraction failed: ${error}`);
result.isValid = false;
}
return result;
}
}
interface ValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}Bundle optimization for smaller VSIX sizes:
// webpack.config.js for extension bundling
const path = require('path');
module.exports = {
target: 'node',
mode: 'production',
entry: './src/extension.ts',
output: {
path: path.resolve(__dirname, 'out'),
filename: 'extension.js',
libraryTarget: 'commonjs2'
},
externals: {
vscode: 'commonjs vscode'
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: 'ts-loader'
}
]
},
optimization: {
minimize: true
}
};Package.json adaptation for Webpack:
{
"main": "./out/extension.js",
"scripts": {
"webpack": "webpack --mode production",
"webpack:dev": "webpack --mode development --watch",
"vscode:prepublish": "npm run webpack"
},
"devDependencies": {
"webpack": "^5.75.0",
"webpack-cli": "^5.0.0",
"ts-loader": "^9.4.0"
}
}| Aspect | Java/Maven | VSCode/vsce |
|---|---|---|
| Output format | JAR/WAR | VSIX |
| Dependencies | Maven Dependencies | npm + Extension Dependencies |
| Manifest | META-INF/MANIFEST.MF | extension.vsixmanifest |
| Metadata | pom.xml | package.json |
| Repository | Maven Central | VS Marketplace |
| Installation | Classpath | Extension Host |
Packaging Pipeline: Always run tests before packaging and implement validation.
Versioning: Use semantic versioning and Git tags for releases.
Bundle Size: Exclude unnecessary files via
.vsixignore and use Webpack for code bundling.
Dependency Management: Minimize runtime dependencies and rely on extension-specific APIs.
Enterprise Distribution: Use internal artifact repositories for VSIX distribution instead of public marketplace.
Automated Builds: Implement CI/CD pipeline for automatic testing, packaging, and distribution.
Validation: Automatically validate VSIX packages before distribution.
Documentation: Include README and CHANGELOG in VSIX for better user experience.
# Complete release workflow
npm run lint && npm test && npm run package && npm run validate-vsix