public async writeVsix()

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);
			});
		});
	}