UNPKG

vsix-utils

Version:

utilities for working with VSIX packages

128 lines (125 loc) 3.83 kB
import { isInMemoryFile } from './chunk-M7VDGRNO.mjs'; import { Buffer } from 'node:buffer'; import { existsSync, createWriteStream } from 'node:fs'; import { unlink, constants } from 'node:fs/promises'; import { XMLParser } from 'fast-xml-parser'; import yauzl from 'yauzl'; import yazl from 'yazl'; async function writeVsix(options) { const { files, packagePath, force, epoch } = options; if (!files || files.length === 0) { throw new Error("no files specified to package"); } if (!packagePath) { throw new Error("no package path specified"); } if (existsSync(packagePath)) { if (!force) { throw new Error(`package already exists at ${packagePath}`); } await unlink(packagePath); } const zip = new yazl.ZipFile(); const zipOptions = {}; if (epoch != null) { zipOptions.mtime = new Date(epoch * 1e3); files.sort((a, b) => a.path.localeCompare(b.path)); } try { for (const f of files) { if (isInMemoryFile(f)) { zip.addBuffer( typeof f.contents === "string" ? Buffer.from(f.contents, "utf8") : f.contents, f.path, { ...zipOptions } ); continue; } zip.addFile(f.localPath, f.path, { ...zipOptions }); } zip.end(); const zipStream = createWriteStream(packagePath, { mode: constants.S_IRWXU | constants.S_IRGRP | constants.S_IXGRP | constants.S_IROTH | constants.S_IXOTH }); zip.outputStream.pipe(zipStream); await new Promise((resolve, reject) => { zipStream.once("finish", resolve); zipStream.once("error", reject); zip.once("error", (err) => { zipStream.destroy(); reject(err); }); }); return true; } catch (err) { if (existsSync(packagePath)) { await unlink(packagePath); } if (err instanceof Error) { throw new TypeError(`failed to create package: ${err.message}`); } throw new Error(`failed to create package: ${err}`); } } async function readVsix(options) { const { packagePath } = options; const zip = await new Promise((resolve, reject) => { yauzl.open(packagePath, { lazyEntries: true }, (err, zip2) => { if (err) { reject(err); } else { resolve(zip2); } }); }); const files = await new Promise((resolve, reject) => { const fileMap = /* @__PURE__ */ new Map(); zip.once("close", () => resolve(fileMap)); zip.on("entry", async (entry) => { try { const buffer = await new Promise((resolve2, reject2) => { zip.openReadStream(entry, (err, stream) => { if (err) reject2(err); stream.once("error", reject2); stream.once("end", () => { if (stream.readable) { reject2(new Error("Stream ended before all data was read")); } }); return resolve2(bufferStream(stream)); }); }); fileMap.set(entry.fileName, buffer); zip.readEntry(); } catch (err) { zip.close(); reject(err); } }); zip.readEntry(); }); if (!files.has("extension.vsixmanifest")) { throw new Error("extension.vsixmanifest file is missing"); } const rawManifest = files.get("extension.vsixmanifest").toString("utf8"); const parser = new XMLParser({ preserveOrder: false, transformTagName(tagName) { return tagName.toLowerCase(); } }); const manifest = parser.parse(rawManifest); return { files, manifest }; } async function bufferStream(stream) { return await new Promise((resolve, reject) => { const buffers = []; stream.on("data", (buffer) => buffers.push(buffer)); stream.once("error", reject); stream.once("end", () => resolve(Buffer.concat(buffers))); }); } export { readVsix, writeVsix };