in app/exec/extension/_lib/vsix-writer.ts [123:265]
public async writeVsix(): Promise<string> {
if (this.settings.metadataOnly) {
const outputPath = this.settings.outputPath;
const pathExists = await exists(outputPath);
if (pathExists && !(await promisify(lstat)(outputPath)).isDirectory()) {
throw new Error("--output-path must be a directory when using --metadata-only.");
}
if (!pathExists) {
await promisify(mkdirp)(outputPath, undefined);
}
for (const builder of this.manifestBuilders) {
for (const filePath of Object.keys(builder.files)) {
const fileObj = builder.files[filePath];
if (fileObj.isMetadata) {
const content = fileObj.content || (await promisify(readFile)(fileObj.path, "utf-8"));
const writePath = path.join(this.settings.outputPath, fileObj.partName);
const folder = path.dirname(writePath);
await promisify(mkdirp)(folder, undefined);
await promisify(writeFile)(writePath, content, "utf-8");
}
}
}
return this.writeVsixMetadata();
}
let outputPath = this.getOutputPath(this.settings.outputPath);
let vsix = new zip();
let builderPromises: Promise<void>[] = [];
let seenPartNames = new Set();
this.manifestBuilders.forEach(builder => {
// Avoid the error EMFILE: too many open files
const addPackageFilesBatch = (
paths: string[],
numBatch: number,
batchSize: number,
deferred?: Deferred<void>,
): Promise<void> => {
deferred = deferred || defer<void>();
let readFilePromises = [];
const start = numBatch * batchSize;
const end = Math.min(paths.length, start + batchSize);
for (let i = start; i < end; i++) {
const path = paths[i];
let itemName = toZipItemName(builder.files[path].partName);
if (!VsixWriter.validatePartName(itemName)) {
let eol = require("os").EOL;
throw new Error(
"Part Name '" +
itemName +
"' is invalid. Please check the following: " +
eol +
"1. No whitespace or any of these characters: #^[]<>?" +
eol +
"2. Cannot end with a period." +
eol +
"3. No percent-encoded / or \\ characters. Additionally, % must be followed by two hex characters.",
);
}
if (itemName.indexOf(" "))
if (!builder.files[path].content) {
let readFilePromise = promisify(readFile)(path).then(result => {
if (!seenPartNames.has(itemName)) {
vsix.file(itemName, result);
seenPartNames.add(itemName);
}
if ((builder.files[path] as any)._additionalPackagePaths) {
for (const p of (builder.files[path] as any)._additionalPackagePaths) {
let additionalItemName = toZipItemName(p);
if (!seenPartNames.has(additionalItemName)) {
vsix.file(additionalItemName, result);
seenPartNames.add(additionalItemName);
}
}
}
});
readFilePromises.push(readFilePromise);
} else {
if (!seenPartNames.has(itemName)) {
vsix.file(itemName, builder.files[path].content);
seenPartNames.add(itemName);
}
if ((builder.files[path] as any)._additionalPackagePaths) {
for (const p of (builder.files[path] as any)._additionalPackagePaths) {
let additionalItemName = toZipItemName(p);
if (!seenPartNames.has(additionalItemName)) {
vsix.file(additionalItemName, builder.files[path].content);
seenPartNames.add(additionalItemName);
}
}
}
readFilePromises.push(Promise.resolve<void>(null));
}
}
Promise.all(readFilePromises)
.then(function() {
if (end < paths.length) {
// Next batch
addPackageFilesBatch(paths, numBatch + 1, batchSize, deferred);
} else {
deferred.resolve(null);
}
})
.catch(function(err) {
deferred.reject(err);
});
return deferred.promise;
};
// Add the package files in batches
let builderPromise = addPackageFilesBatch(Object.keys(builder.files), 0, VsixWriter.VSIX_ADD_FILES_BATCH_SIZE).then(
() => {
// Add the manifest itself
vsix.file(toZipItemName(builder.getPath()), builder.getResult(this.resources.combined));
},
);
builderPromises.push(builderPromise);
});
return Promise.all(builderPromises).then(() => {
trace.debug("Writing vsix to: %s", outputPath);
return new Promise((resolve, reject) => {
mkdirp(path.dirname(outputPath), (err, made) => {
if (err) {
reject(err);
} else {
resolve(made);
}
});
}).then(async () => {
let buffer = await vsix.generateAsync({
type: "nodebuffer",
compression: "DEFLATE",
});
return promisify(writeFile)(outputPath, buffer).then(() => outputPath);
});
});
}