From 92d4b2f33d0c37c2ec9ce0b5597b308afe383120 Mon Sep 17 00:00:00 2001 From: thinknathan Date: Tue, 19 Dec 2023 15:34:27 -0800 Subject: [PATCH] Allow providing a folder --- @types/types.d.ts | 6 ++ README.md | 21 +++--- package.json | 9 ++- slice.cjs | 109 +++++++++++++++----------------- src/slice.ts | 130 ++++++++++++++++---------------------- src/utils/processImage.ts | 105 ++++++++++++++++++++++++++++++ src/utils/processPath.ts | 35 ++++++++++ src/utils/workerPool.ts | 80 +++++++++++++++++++++++ utils/processImage.js | 82 ++++++++++++++++++++++++ utils/processPath.js | 30 +++++++++ utils/workerPool.js | 75 ++++++++++++++++++++++ yarn.lock | 118 ++++++++++++++++++++++++++++++++++ 12 files changed, 654 insertions(+), 146 deletions(-) create mode 100644 @types/types.d.ts create mode 100644 src/utils/processImage.ts create mode 100644 src/utils/processPath.ts create mode 100644 src/utils/workerPool.ts create mode 100644 utils/processImage.js create mode 100644 utils/processPath.js create mode 100644 utils/workerPool.js diff --git a/@types/types.d.ts b/@types/types.d.ts new file mode 100644 index 0000000..c89eb53 --- /dev/null +++ b/@types/types.d.ts @@ -0,0 +1,6 @@ +declare type Options = { + filename?: string; + folderPath?: string; + width: number; + height?: number; +}; diff --git a/README.md b/README.md index db9682a..9c702c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # node-image-slice -Command-line utility that slices an input image into segments according to specified width and height, and outputs those segments into a folder. +Command-line utility that slices input images into segments according to specified width and height, and outputs those segments into a folder. ## Install @@ -16,18 +16,17 @@ Command-line utility that slices an input image into segments according to speci ## Usage -`node slice.cjs [height]` +`node slice.cjs` -- If `filename` does not include an extension, `.png` will be guessed -- If `height` is not specified, `width` will be used as `height` - -## Supported formats +``` +-f, --filename Input image filename [string] +-i, --folderPath Input folder [string] +-w, --width Output image width [number] [required] +-h, --height Output image height [number] +``` -- jpeg -- png -- bmp -- tiff -- gif +- If `filename` does not include an extension, `.png`, `.gif`, `.jpg` and `.jpeg` will be guessed +- If `height` is not specified, `width` will be used as `height` ## Background diff --git a/package.json b/package.json index 3f362fd..cc6b179 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-image-slice", - "version": "1.0.0", + "version": "2.0.0", "description": "Slices an input image into segments according to specified width and height", "repository": { "type": "git", @@ -11,7 +11,8 @@ "type": "commonjs", "main": "slice.cjs", "files": [ - "slice.cjs" + "slice.cjs", + "utils" ], "scripts": { "build": "tsc && npm run renameCjs && npm run prettier", @@ -19,11 +20,13 @@ "prettier": "prettier \"./**/*.{ts,d.ts,cjs,md,json}\" --write" }, "devDependencies": { + "@types/yargs": "^17.0.32", "prettier": "^3.1.1", "tsc": "^2.0.4", "typescript": "~5.2.2" }, "dependencies": { - "jimp": "~0.22.10" + "jimp": "~0.22.10", + "yargs": "^17.7.2" } } diff --git a/slice.cjs b/slice.cjs index d2efd70..7166c86 100644 --- a/slice.cjs +++ b/slice.cjs @@ -1,61 +1,56 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const Jimp = require("jimp"); -const fs = require("fs"); -const path = require("path"); -const outputFolder = "output"; -function sliceImage(filename, width, height) { - Jimp.read(filename, (err, image) => { - if (err) { - // Try again by appending '.png' to the filename - const pngFilename = `${filename}.png`; - Jimp.read(pngFilename, (pngErr, pngImage) => { - if (pngErr) { - console.error("Error reading the image:", pngErr); - return; - } - continueSlicing(pngImage, width, height, filename); - }); - } else { - continueSlicing(image, width, height, filename); - } - }); -} -function continueSlicing(image, width, height, inputFilename) { - // If height is not specified, use width as height - height = height || width; - const imageWidth = image.getWidth(); - const imageHeight = image.getHeight(); - // Calculate the number of slices in both dimensions - const horizontalSlices = Math.ceil(imageWidth / width); - const verticalSlices = Math.ceil(imageHeight / height); - // Create a folder for output if it doesn't exist - if (!fs.existsSync(outputFolder)) { - fs.mkdirSync(outputFolder); - } - // Slice the image and save each segment - for (let y = 0; y < verticalSlices; y++) { - for (let x = 0; x < horizontalSlices; x++) { - const startX = x * width; - const startY = y * height; - const sliceWidth = Math.min(width, imageWidth - startX); - const sliceHeight = Math.min(height, imageHeight - startY); - const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight); - // Incorporate the input filename into the output filename - const baseFilename = path.basename( - inputFilename, - path.extname(inputFilename), - ); - const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`; - slice.write(outputFilename); - console.log(`Slice saved: ${outputFilename}`); - } +const yargs = require("yargs"); +const os = require("os"); +const processImage_1 = require("./utils/processImage"); +const processPath_1 = require("./utils/processPath"); +// Parse command line arguments +const options = yargs + .option("f", { + alias: "filename", + describe: "Input image filename", + type: "string", + }) + .option("i", { + alias: "folderPath", + describe: "Input folder", + type: "string", + }) + .option("w", { + alias: "width", + describe: "Output image width", + type: "number", + demandOption: true, + coerce: (value) => { + if (value < 1) { + throw new Error("width should not be lower than 1"); + } + return Math.round(value); + }, + }) + .option("h", { + alias: "height", + describe: "Output image height", + type: "number", + coerce: (value) => { + if (value !== undefined && value < 1) { + throw new Error("height should not be lower than 1"); + } + return Math.round(value); + }, + }).argv; +if (options.filename) { + // Process a single image + const { filename, width, height } = options; + (0, processImage_1.sliceImage)(filename, width, height); +} else if (options.folderPath) { + // Process all images in a folder, splitting the task into threads + let numCores = 2; + try { + numCores = os.cpus().length; + } catch (err) { + console.error(err); } -} -// Get input from the command line arguments -const [filename, width, height] = process.argv.slice(2); -if (!filename || !width) { - console.log("Usage: node slice.cjs [height]"); -} else { - sliceImage(filename, parseInt(width), parseInt(height)); + numCores = Math.max(numCores - 1, 1); + (0, processPath_1.processPath)(options.folderPath, options, numCores); } diff --git a/src/slice.ts b/src/slice.ts index db0f3f3..6667c73 100644 --- a/src/slice.ts +++ b/src/slice.ts @@ -1,77 +1,57 @@ -import * as Jimp from "jimp"; -import * as fs from "fs"; -import * as path from "path"; - -const outputFolder = "output"; - -function sliceImage(filename: string, width: number, height?: number): void { - Jimp.read(filename, (err, image) => { - if (err) { - // Try again by appending '.png' to the filename - const pngFilename = `${filename}.png`; - Jimp.read(pngFilename, (pngErr, pngImage) => { - if (pngErr) { - console.error("Error reading the image:", pngErr); - return; - } - continueSlicing(pngImage, width, height, filename); - }); - } else { - continueSlicing(image, width, height, filename); - } - }); -} - -function continueSlicing( - image: Jimp, - width: number, - height: number | undefined, - inputFilename: string, -): void { - // If height is not specified, use width as height - height = height || width; - - const imageWidth = image.getWidth(); - const imageHeight = image.getHeight(); - - // Calculate the number of slices in both dimensions - const horizontalSlices = Math.ceil(imageWidth / width); - const verticalSlices = Math.ceil(imageHeight / height); - - // Create a folder for output if it doesn't exist - if (!fs.existsSync(outputFolder)) { - fs.mkdirSync(outputFolder); - } - - // Slice the image and save each segment - for (let y = 0; y < verticalSlices; y++) { - for (let x = 0; x < horizontalSlices; x++) { - const startX = x * width; - const startY = y * height; - - const sliceWidth = Math.min(width, imageWidth - startX); - const sliceHeight = Math.min(height, imageHeight - startY); - - const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight); - - // Incorporate the input filename into the output filename - const baseFilename = path.basename( - inputFilename, - path.extname(inputFilename), - ); - const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`; - - slice.write(outputFilename); - console.log(`Slice saved: ${outputFilename}`); - } +import * as yargs from "yargs"; +import * as os from "os"; + +import { sliceImage } from "./utils/processImage"; +import { processPath } from "./utils/processPath"; + +// Parse command line arguments +const options = yargs + .option("f", { + alias: "filename", + describe: "Input image filename", + type: "string", + }) + .option("i", { + alias: "folderPath", + describe: "Input folder", + type: "string", + }) + .option("w", { + alias: "width", + describe: "Output image width", + type: "number", + demandOption: true, + coerce: (value) => { + if (value < 1) { + throw new Error("width should not be lower than 1"); + } + return Math.round(value); + }, + }) + .option("h", { + alias: "height", + describe: "Output image height", + type: "number", + coerce: (value) => { + if (value !== undefined && value < 1) { + throw new Error("height should not be lower than 1"); + } + return Math.round(value); + }, + }).argv as unknown as Options; + +if (options.filename) { + // Process a single image + const { filename, width, height } = options; + sliceImage(filename, width, height); +} else if (options.folderPath) { + // Process all images in a folder, splitting the task into threads + let numCores = 2; + try { + numCores = os.cpus().length; + } catch (err) { + console.error(err); } -} - -// Get input from the command line arguments -const [filename, width, height] = process.argv.slice(2); - -if (!filename || !width) { - console.log("Usage: node slice.cjs [height]"); -} else { - sliceImage(filename, parseInt(width), parseInt(height)); + numCores = Math.max(numCores - 1, 1); + processPath(options.folderPath, options, numCores); } diff --git a/src/utils/processImage.ts b/src/utils/processImage.ts new file mode 100644 index 0000000..da79b38 --- /dev/null +++ b/src/utils/processImage.ts @@ -0,0 +1,105 @@ +import * as Jimp from "jimp"; +import * as fs from "fs"; +import * as path from "path"; +import { workerData, isMainThread } from "worker_threads"; + +const outputFolder = "output"; + +/** + * Function to slice an image into smaller segments + */ +export function sliceImage( + filename: string, + width: number, + height?: number, + skipExtCheck?: boolean, +): void { + Jimp.read(filename, (err, image) => { + if (err && skipExtCheck) { + console.error(err); + } else { + // Continue slicing if image is successfully read + if (image) { + continueSlicing(image, width, height, filename); + return; + } + } + }); + + if (skipExtCheck) { + return; + } + + // Check for supported image formats if skipExtCheck is false + const supportedFormats = [".png", ".gif", ".jpg", ".jpeg"]; + let foundImage = false; + + // Attempt to read the image with different extensions + supportedFormats.forEach((ext) => { + const fullFilename = filename + ext; + if (!foundImage) { + Jimp.read(fullFilename, (err, image) => { + if (!foundImage && !err) { + foundImage = true; + continueSlicing(image, width, height, fullFilename); + } + }); + } + }); +} + +/** + * Continue slicing the image into smaller segments + */ +function continueSlicing( + image: Jimp, + width: number, + height: number | undefined, + inputFilename: string, +): void { + // If height is not specified, use width as height + height = height || width; + + const imageWidth = image.getWidth(); + const imageHeight = image.getHeight(); + + // Calculate the number of slices in both dimensions + const horizontalSlices = Math.ceil(imageWidth / width); + const verticalSlices = Math.ceil(imageHeight / height); + + // Create a folder for output if it doesn't exist + if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder); + } + + // Slice the image and save each segment + for (let y = 0; y < verticalSlices; y++) { + for (let x = 0; x < horizontalSlices; x++) { + const startX = x * width; + const startY = y * height; + + const sliceWidth = Math.min(width, imageWidth - startX); + const sliceHeight = Math.min(height, imageHeight - startY); + + const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight); + + // Incorporate the input filename into the output filename + const baseFilename = path.basename( + inputFilename, + path.extname(inputFilename), + ); + const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`; + + slice.write(outputFilename); + console.log(`Slice saved: ${outputFilename}`); + } + } +} + +// If used as a worker thread, get file name from message +if (!isMainThread) { + const { filePath, options } = workerData; + options.filename = filePath; + const { filename, width, height } = options; + sliceImage(filename, width, height, true); +} diff --git a/src/utils/processPath.ts b/src/utils/processPath.ts new file mode 100644 index 0000000..a8f717d --- /dev/null +++ b/src/utils/processPath.ts @@ -0,0 +1,35 @@ +import * as fs from "fs"; +import * as path from "path"; +import { WorkerPool } from "./workerPool"; + +/** + * Processes all files in the specified directory with the given image processing options. + */ +export function processPath( + directoryPath: string, + options: Options, + maxWorkers: number, +): void { + const workerPool = new WorkerPool(maxWorkers); + + // Read the contents of the directory + fs.readdir(directoryPath, (err, files) => { + if (err) { + console.error(`Error reading directory: ${directoryPath}`, err); + return; + } + + // Add each file as a task to the worker pool + files.forEach((file) => { + const filePath = path.join(directoryPath, file); + + // Check if it's a file (not a subdirectory) + if (fs.statSync(filePath).isFile()) { + workerPool.addTask(filePath, options); + } + }); + + // Wait for all tasks to complete before exiting + workerPool.waitForCompletion(); + }); +} diff --git a/src/utils/workerPool.ts b/src/utils/workerPool.ts new file mode 100644 index 0000000..4d3295b --- /dev/null +++ b/src/utils/workerPool.ts @@ -0,0 +1,80 @@ +import { Worker } from "worker_threads"; +import * as path from "path"; + +/** + * Manages a pool of worker threads for parallel processing of image files. + */ +export class WorkerPool { + private workers: Worker[] = []; + private taskQueue: { filePath: string; options: Options }[] = []; + private maxWorkers: number; + + /** + * Creates a new WorkerPool instance. + * + * @param maxWorkers - The maximum number of worker threads in the pool. + */ + constructor(maxWorkers: number) { + this.maxWorkers = maxWorkers; + } + + /** + * Creates a worker thread for processing a specific file with given options. + * + * @param filePath - The path of the file to process. + * @param options - Image processing options for the file. + */ + private createWorker(filePath: string, options: Options): void { + const worker = new Worker(path.join(__dirname, "processImage.js"), { + workerData: { filePath, options }, + }); + + // Listen for messages and errors from the worker + worker.on("message", (message) => { + console.log(message); + this.processNextTask(); + }); + + worker.on("error", (err) => { + console.error(`Error in worker for file ${filePath}:`, err); + this.processNextTask(); + }); + + this.workers.push(worker); + } + + /** + * Processes the next task in the queue. + */ + private processNextTask(): void { + const nextTask = this.taskQueue.shift(); + if (nextTask) { + this.createWorker(nextTask.filePath, nextTask.options); + } + } + + /** + * Adds a task to the worker pool for processing. + * + * @param filePath - The path of the file to process. + * @param options - Image processing options for the file. + */ + public addTask(filePath: string, options: Options): void { + if (this.workers.length < this.maxWorkers) { + this.createWorker(filePath, options); + } else { + this.taskQueue.push({ filePath, options }); + } + } + + /** + * Waits for all tasks to complete before exiting. + */ + public waitForCompletion(): void { + this.workers.forEach((worker) => { + worker.on("exit", () => { + this.processNextTask(); + }); + }); + } +} diff --git a/utils/processImage.js b/utils/processImage.js new file mode 100644 index 0000000..9a77eef --- /dev/null +++ b/utils/processImage.js @@ -0,0 +1,82 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.sliceImage = void 0; +const Jimp = require("jimp"); +const fs = require("fs"); +const path = require("path"); +const worker_threads_1 = require("worker_threads"); +const outputFolder = "output"; +/** + * Function to slice an image into smaller segments + */ +function sliceImage(filename, width, height, skipExtCheck) { + Jimp.read(filename, (err, image) => { + if (err && skipExtCheck) { + console.error(err); + } + else { + // Continue slicing if image is successfully read + if (image) { + continueSlicing(image, width, height, filename); + return; + } + } + }); + if (skipExtCheck) { + return; + } + // Check for supported image formats if skipExtCheck is false + const supportedFormats = [".png", ".gif", ".jpg", ".jpeg"]; + let foundImage = false; + // Attempt to read the image with different extensions + supportedFormats.forEach((ext) => { + const fullFilename = filename + ext; + if (!foundImage) { + Jimp.read(fullFilename, (err, image) => { + if (!foundImage && !err) { + foundImage = true; + continueSlicing(image, width, height, fullFilename); + } + }); + } + }); +} +exports.sliceImage = sliceImage; +/** + * Continue slicing the image into smaller segments + */ +function continueSlicing(image, width, height, inputFilename) { + // If height is not specified, use width as height + height = height || width; + const imageWidth = image.getWidth(); + const imageHeight = image.getHeight(); + // Calculate the number of slices in both dimensions + const horizontalSlices = Math.ceil(imageWidth / width); + const verticalSlices = Math.ceil(imageHeight / height); + // Create a folder for output if it doesn't exist + if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder); + } + // Slice the image and save each segment + for (let y = 0; y < verticalSlices; y++) { + for (let x = 0; x < horizontalSlices; x++) { + const startX = x * width; + const startY = y * height; + const sliceWidth = Math.min(width, imageWidth - startX); + const sliceHeight = Math.min(height, imageHeight - startY); + const slice = image.clone().crop(startX, startY, sliceWidth, sliceHeight); + // Incorporate the input filename into the output filename + const baseFilename = path.basename(inputFilename, path.extname(inputFilename)); + const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`; + slice.write(outputFilename); + console.log(`Slice saved: ${outputFilename}`); + } + } +} +// If used as a worker thread, get file name from message +if (!worker_threads_1.isMainThread) { + const { filePath, options } = worker_threads_1.workerData; + options.filename = filePath; + const { filename, width, height } = options; + sliceImage(filename, width, height, true); +} diff --git a/utils/processPath.js b/utils/processPath.js new file mode 100644 index 0000000..68c7020 --- /dev/null +++ b/utils/processPath.js @@ -0,0 +1,30 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.processPath = void 0; +const fs = require("fs"); +const path = require("path"); +const workerPool_1 = require("./workerPool"); +/** + * Processes all files in the specified directory with the given image processing options. + */ +function processPath(directoryPath, options, maxWorkers) { + const workerPool = new workerPool_1.WorkerPool(maxWorkers); + // Read the contents of the directory + fs.readdir(directoryPath, (err, files) => { + if (err) { + console.error(`Error reading directory: ${directoryPath}`, err); + return; + } + // Add each file as a task to the worker pool + files.forEach((file) => { + const filePath = path.join(directoryPath, file); + // Check if it's a file (not a subdirectory) + if (fs.statSync(filePath).isFile()) { + workerPool.addTask(filePath, options); + } + }); + // Wait for all tasks to complete before exiting + workerPool.waitForCompletion(); + }); +} +exports.processPath = processPath; diff --git a/utils/workerPool.js b/utils/workerPool.js new file mode 100644 index 0000000..ad1416f --- /dev/null +++ b/utils/workerPool.js @@ -0,0 +1,75 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WorkerPool = void 0; +const worker_threads_1 = require("worker_threads"); +const path = require("path"); +/** + * Manages a pool of worker threads for parallel processing of image files. + */ +class WorkerPool { + /** + * Creates a new WorkerPool instance. + * + * @param maxWorkers - The maximum number of worker threads in the pool. + */ + constructor(maxWorkers) { + this.workers = []; + this.taskQueue = []; + this.maxWorkers = maxWorkers; + } + /** + * Creates a worker thread for processing a specific file with given options. + * + * @param filePath - The path of the file to process. + * @param options - Image processing options for the file. + */ + createWorker(filePath, options) { + const worker = new worker_threads_1.Worker(path.join(__dirname, "processImage.js"), { + workerData: { filePath, options }, + }); + // Listen for messages and errors from the worker + worker.on("message", (message) => { + console.log(message); + this.processNextTask(); + }); + worker.on("error", (err) => { + console.error(`Error in worker for file ${filePath}:`, err); + this.processNextTask(); + }); + this.workers.push(worker); + } + /** + * Processes the next task in the queue. + */ + processNextTask() { + const nextTask = this.taskQueue.shift(); + if (nextTask) { + this.createWorker(nextTask.filePath, nextTask.options); + } + } + /** + * Adds a task to the worker pool for processing. + * + * @param filePath - The path of the file to process. + * @param options - Image processing options for the file. + */ + addTask(filePath, options) { + if (this.workers.length < this.maxWorkers) { + this.createWorker(filePath, options); + } + else { + this.taskQueue.push({ filePath, options }); + } + } + /** + * Waits for all tasks to complete before exiting. + */ + waitForCompletion() { + this.workers.forEach((worker) => { + worker.on("exit", () => { + this.processNextTask(); + }); + }); + } +} +exports.WorkerPool = WorkerPool; diff --git a/yarn.lock b/yarn.lock index f0b5a22..4cf32ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -269,6 +269,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.1.tgz#0611b37db4246c937feef529ddcc018cf8e35708" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.32": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + any-base@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" @@ -297,11 +321,42 @@ buffer@^5.2.0: base64-js "^1.3.1" ieee754 "^1.1.13" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + exif-parser@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" @@ -316,6 +371,11 @@ file-type@^16.5.4: strtok3 "^6.2.4" token-types "^4.1.1" +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + gifwrap@^0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/gifwrap/-/gifwrap-0.10.1.tgz#9ed46a5d51913b482d4221ce9c727080260b681e" @@ -349,6 +409,11 @@ inherits@^2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-function@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" @@ -501,6 +566,11 @@ regenerator-runtime@^0.13.3: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -511,6 +581,15 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -518,6 +597,13 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strtok3@^6.2.4: version "6.3.0" resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-6.3.0.tgz#358b80ffe6d5d5620e19a073aa78ce947a90f9a0" @@ -589,6 +675,15 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + xhr@^2.0.1: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" @@ -621,3 +716,26 @@ xtend@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1"