@ipp/cli
Version:
An image build orchestrator for the modern web
113 lines (112 loc) • 4.66 kB
JavaScript
;
/**
* Image Processing Pipeline - Copyright (c) Marcus Cemes
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.startCli = void 0;
const constants_1 = require("constants");
const fs_1 = require("fs");
const path_1 = require("path");
const constants_2 = require("./constants");
const context_1 = require("./lib/context");
const exception_1 = require("./lib/exception");
const state_1 = require("./lib/state");
const buffer_1 = require("./lib/stream/operators/buffer");
const passthrough_1 = require("./lib/stream/operators/passthrough");
const to_promise_1 = require("./lib/stream/operators/to_promise");
const counters_1 = require("./operators/counters");
const exceptions_1 = require("./operators/exceptions");
const manifest_1 = require("./operators/manifest");
const process_1 = require("./operators/process");
const save_1 = require("./operators/save");
const search_1 = require("./operators/search");
const ui_1 = require("./ui");
const MANIFEST_FILE = "manifest.json";
const BUFFER_SIZE = 8;
async function startCli(config, ui = ui_1.DynamicUI) {
return withCliContext(config.concurrency, !!config.manifest, !!config.clean, constants_2.VERSION, ui, async (ctx) => {
try {
// Unregister handler to allow force quitting
ctx.interrupt.rejecter.catch(() => {
ctx.interrupt.destroy();
setStatus(ctx, state_1.Status.INTERRUPT);
});
if (config.clean)
await deleteDirectory(config.output);
await ensureOutputPath(config.output);
setStatus(ctx, state_1.Status.PROCESSING);
await createPipeline(ctx, config, MANIFEST_FILE).pipe((0, to_promise_1.toPromise)());
setStatus(ctx, state_1.Status.COMPLETE);
}
catch (err) {
setStatus(ctx, state_1.Status.ERROR);
throw err;
}
});
}
exports.startCli = startCli;
async function withCliContext(concurrency, manifest, clean, version, ui, fn) {
const ctx = (0, context_1.createContext)(concurrency, manifest, clean, version, ui);
try {
await fn(ctx);
}
finally {
await ctx.ui.stop(ctx.state.complete());
ctx.interrupt.destroy();
}
}
function createPipeline(ctx, config, manifestFile) {
const paths = typeof config.input === "string" ? [config.input] : config.input;
return (0, search_1.searchForImages)(paths)
.pipe((0, counters_1.sourceCounter)(ctx))
.pipe((0, buffer_1.buffer)(BUFFER_SIZE))
.pipe((0, process_1.processImages)(config.pipeline, config.concurrency))
.pipe((0, counters_1.completedCounter)(ctx))
.pipe((0, buffer_1.buffer)(BUFFER_SIZE))
.pipe((0, save_1.saveImages)(config.output, !!config.flat))
.pipe(config.manifest
? (0, manifest_1.saveManifest)((0, path_1.resolve)(config.output, manifestFile), config.manifest)
: (0, passthrough_1.passthrough)())
.pipe((0, counters_1.exceptionCounter)(ctx))
.pipe(config.suppressErrors ? (0, passthrough_1.passthrough)() : (0, exceptions_1.exceptionHandler)(config.output, config.errorOutput));
}
function setStatus(ctx, status) {
ctx.state.update((state) => (state.status = status));
}
async function ensureOutputPath(path) {
try {
await fs_1.promises.access(path, constants_1.W_OK);
}
catch (err) {
await fs_1.promises.mkdir(path);
}
}
async function deleteDirectory(path) {
try {
const stat = await fs_1.promises.stat(path);
if (!stat.isDirectory()) {
throw new exception_1.CliException("Output clean error", exception_1.CliExceptionCode.CLEAN, "Output clean error", "The output path already exists but is not a directory.\n" +
"Please remove the file and try again.\n");
}
}
catch (error) {
if (hasErrorCode(error, "ENOENT"))
return;
throw error;
}
try {
await fs_1.promises.rm(path, { recursive: true });
}
catch (err) {
throw new exception_1.CliException("Output clean error:\n" + err.message, exception_1.CliExceptionCode.CLEAN, "Output clean error", "An error occurred while trying to clean the out directory.\n" +
"You may not have sufficient permissions to do so.\n" +
"You can disable output cleaning in the config file.\n\n" +
String(err));
}
}
function hasErrorCode(error, code) {
return error instanceof Error && error.code === code;
}