vsix-extension-manager
Version:
VSIX Extension Manager: A comprehensive CLI tool to download, export, import, and manage VS Code/Cursor extensions as VSIX files
233 lines • 10.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fromList = fromList;
const p = __importStar(require("@clack/prompts"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const import_1 = require("../features/import");
const download_1 = require("../features/download");
const helpers_1 = require("../core/helpers");
const constants_1 = require("../config/constants");
async function fromList(options) {
try {
p.intro("📥 Download from Extension List");
// Get input file
let filePath = options.file;
if (!filePath) {
filePath = (await p.text({
message: "Enter path to extensions list file:",
validate: (input) => {
if (!input.trim())
return "Please enter a file path";
if (!fs_1.default.existsSync(input.trim()))
return "File does not exist";
return undefined;
},
}));
if (p.isCancel(filePath)) {
p.cancel("Operation cancelled.");
process.exit(0);
}
}
// Validate file exists
if (!fs_1.default.existsSync(filePath)) {
p.log.error(`❌ File not found: ${filePath}`);
process.exit(1);
}
// Read file content
const content = fs_1.default.readFileSync(filePath, "utf-8");
// Determine format from file extension if not specified
let format = options.format;
if (!format) {
const ext = path_1.default.extname(filePath).toLowerCase();
if (ext === ".json") {
// Only accept VS Code extensions.json
try {
const parsed = JSON.parse(content);
format = parsed.recommendations ? "extensions.json" : undefined;
}
catch {
format = undefined;
}
}
else {
format = "txt";
}
}
if (format === "json") {
p.log.error("❌ JSON arrays of IDs are no longer supported. Use txt (one ID per line) or VS Code extensions.json.");
process.exit(1);
}
// Parse extensions list
let extensionIds;
try {
extensionIds = (0, import_1.parseExtensionsList)(content, format, filePath);
}
catch (error) {
p.log.error(`❌ Failed to parse file: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
}
if (extensionIds.length === 0) {
p.log.warn("⚠️ No extensions found in the file");
return;
}
p.log.info(`📦 Found ${extensionIds.length} extension(s) to download`);
// Convert extension IDs to bulk download format
const bulkItems = extensionIds.map((id) => {
// Parse publisher.name format
const parts = id.split(".");
if (parts.length < 2) {
throw new Error(`Invalid extension ID format: ${id}. Expected format: publisher.name`);
}
const publisher = parts[0];
const name = parts.slice(1).join(".");
// Construct marketplace URL
const url = `https://marketplace.visualstudio.com/items?itemName=${id}`;
return {
name: `${publisher}.${name}`,
url,
version: "latest", // Use latest by default
};
});
// Create temporary JSON file for bulk download
const tempJsonPath = path_1.default.join(process.cwd(), `.temp-extensions-${Date.now()}.json`);
try {
// Write temporary JSON file
fs_1.default.writeFileSync(tempJsonPath, JSON.stringify(bulkItems, null, 2));
// Prepare bulk download options
const bulkOptions = (0, helpers_1.buildBulkOptionsFromCli)(options, {
parallel: 3,
retry: 3,
retryDelay: 1000,
});
// Determine output directory (cache-dir takes precedence, then output, then prompt)
let outputDir;
if (options.cacheDir) {
outputDir = options.cacheDir;
}
else if (options.output) {
outputDir = options.output;
}
else {
const outputInput = await p.text({
message: "Enter output directory:",
placeholder: "./downloads",
initialValue: constants_1.DEFAULT_OUTPUT_DIR,
});
if (p.isCancel(outputInput)) {
p.cancel("Operation cancelled.");
process.exit(0);
}
outputDir = outputInput.trim() || constants_1.DEFAULT_OUTPUT_DIR;
}
// Show preview in interactive mode
if (!options.quiet && !options.json) {
p.note(extensionIds.slice(0, 10).join("\n") +
(extensionIds.length > 10 ? `\n... and ${extensionIds.length - 10} more` : ""), "Extensions to download");
const proceed = await p.confirm({
message: "Proceed with download?",
});
if (p.isCancel(proceed) || !proceed) {
p.cancel("Operation cancelled.");
process.exit(0);
}
}
// Perform bulk download using temporary file
await (0, download_1.downloadBulkExtensions)(tempJsonPath, outputDir, bulkOptions);
// Install after download if requested
if (options.install && !options.downloadOnly) {
if (!options.quiet) {
p.log.info("🔧 Installing downloaded extensions...");
}
const { getInstallFromListService } = await Promise.resolve().then(() => __importStar(require("../features/install")));
const installService = getInstallFromListService();
// Resolve editor for installation
const { getEditorService } = await Promise.resolve().then(() => __importStar(require("../features/install")));
const editorService = getEditorService();
const availableEditors = await editorService.getAvailableEditors();
if (availableEditors.length === 0) {
p.log.warn("⚠️ No editors found for installation. Extensions downloaded but not installed.");
return;
}
// Auto-select editor (prefer Cursor)
const editor = availableEditors.find((e) => e.name === "cursor") || availableEditors[0];
const binPath = editor.binaryPath;
// Use the same list file for installation
const installResult = await installService.installFromList(binPath, filePath, [outputDir], // Search in the download directory
{
downloadMissing: false, // Already downloaded
installOptions: {
dryRun: false,
skipInstalled: true, // Skip already installed by default
parallel: 1, // Conservative for post-download install
retry: Number(options.retry) || 2,
retryDelay: Number(options.retryDelay) || 1000,
quiet: options.quiet,
},
}, (message) => {
if (!options.quiet) {
p.log.info(`🔧 ${message}`);
}
});
if (!options.quiet) {
p.note(`Downloaded: ${extensionIds.length}\nInstalled: ${installResult.installedExtensions}\nSkipped: ${installResult.skippedExtensions}\nFailed: ${installResult.failedExtensions}`, "Download & Install Summary");
}
}
}
finally {
// Clean up temporary file
if (fs_1.default.existsSync(tempJsonPath)) {
fs_1.default.unlinkSync(tempJsonPath);
}
}
if (!options.quiet) {
if (options.install && !options.downloadOnly) {
p.outro("✨ Download and install completed!");
}
else {
p.outro("✨ Download completed!");
}
}
}
catch (error) {
p.log.error("❌ Error: " + (error instanceof Error ? error.message : String(error)));
process.exit(1);
}
}
//# sourceMappingURL=fromList.js.map