Skip to content

Commit

Permalink
WIP: Fix output to custom directory
Browse files Browse the repository at this point in the history
  • Loading branch information
343dev committed Sep 30, 2024
1 parent 6751791 commit 5f90ba9
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 112 deletions.
12 changes: 7 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import convert from './lib/convert.js';
import { findConfig } from './lib/find-config.js';
import { log, logErrorAndExit } from './lib/log.js';
import optimize from './lib/optimize.js';
import { prepareInputFilePaths } from './lib/prepare-input-file-paths.js';
import { prepareFilePaths } from './lib/prepare-file-paths.js';
import { prepareOutputDirectoryPath } from './lib/prepare-output-directory-path.js';
import { programOptions } from './lib/program-options.js';

Expand Down Expand Up @@ -41,8 +41,11 @@ export default async function optimizt({
const configData = await import(preparedConfigFilePath);
const config = configData.default[currentMode.toLowerCase()];

const preparedInputFilePaths = await prepareInputFilePaths(inputPaths, SUPPORTED_FILE_TYPES[currentMode.toUpperCase()]);
const preparedOutputDirectoryPath = await prepareOutputDirectoryPath(outputDirectoryPath);
const filePaths = await prepareFilePaths({
inputPaths,
outputDirectoryPath: await prepareOutputDirectoryPath(outputDirectoryPath),
extensions: SUPPORTED_FILE_TYPES[currentMode.toUpperCase()],
});

if (isLossless) {
log('Lossless optimization may take a long time');
Expand All @@ -53,8 +56,7 @@ export default async function optimizt({
: optimize;

await processFunction({
inputFilePaths: preparedInputFilePaths,
outputDirectoryPath: preparedOutputDirectoryPath,
filePaths,
config,
});
}
Expand Down
42 changes: 17 additions & 25 deletions lib/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,28 @@ import {
} from './log.js';
import { optionsToArguments } from './options-to-arguments.js';
import { parseImageMetadata } from './parse-image-metadata.js';
import prepareWriteFilePath from './prepare-write-file-path.js';
import { programOptions } from './program-options.js';
import { showTotal } from './show-total.js';

export default async function convert({
inputFilePaths,
outputDirectoryPath,
config,
}) {
export default async function convert({ filePaths, config }) {
const {
isForced,
isLossless,
shouldConvertToAvif,
shouldConvertToWebp,
} = programOptions;

const inputFilePathsCount = inputFilePaths.length;
const filePathsCount = filePaths.length;

if (!inputFilePathsCount) {
if (!filePathsCount) {
return;
}

log(`Converting ${inputFilePathsCount} ${getPlural(inputFilePathsCount, 'image', 'images')} (${isLossless ? 'lossless' : 'lossy'})...`);
log(`Converting ${filePathsCount} ${getPlural(filePathsCount, 'image', 'images')} (${isLossless ? 'lossless' : 'lossy'})...`);

const progressBarTotal = shouldConvertToAvif && shouldConvertToWebp
? inputFilePathsCount * 2
: inputFilePathsCount;
? filePathsCount * 2
: filePathsCount;
const progressBarContainer = createProgressBarContainer(progressBarTotal);
const progressBar = progressBarContainer.create(progressBarTotal, 0);

Expand All @@ -64,13 +59,12 @@ export default async function convert({
: config?.webpGif?.lossy;

const tasksSimultaneousLimit = pLimit(os.cpus().length);
const tasksPromises = inputFilePaths.reduce((accumulator, filePath) => {
const tasksPromises = filePaths.reduce((accumulator, filePath) => {
if (shouldConvertToAvif) {
accumulator.push(
tasksSimultaneousLimit(
() => processFile({
filePath,
outputDirectoryPath,
config: avifConfig || {},
progressBarContainer,
progressBar,
Expand All @@ -88,8 +82,7 @@ export default async function convert({
tasksSimultaneousLimit(
() => processFile({
filePath,
outputDirectoryPath,
config: (path.extname(filePath).toLowerCase() === '.gif'
config: (path.extname(filePath.input).toLowerCase() === '.gif'
? webpGifConfig
: webpConfig)
|| {},
Expand All @@ -116,7 +109,6 @@ export default async function convert({

async function processFile({
filePath,
outputDirectoryPath,
config,
progressBarContainer,
progressBar,
Expand All @@ -126,25 +118,25 @@ async function processFile({
processFunction,
}) {
try {
const { dir, name } = path.parse(filePath);
const { dir, name } = path.parse(filePath.output);
const outputFilePath = path.join(dir, `${name}.${format.toLowerCase()}`);
const preparedOutputFilePath = prepareWriteFilePath(outputFilePath, outputDirectoryPath);

const isAccessible = await checkPathAccessibility(preparedOutputFilePath);
const isAccessible = await checkPathAccessibility(outputFilePath);

if (!isForced && isAccessible) {
logProgressVerbose(preparedOutputFilePath, {
description: `File already exists, '${preparedOutputFilePath}'`,
logProgressVerbose(outputFilePath, {
description: `File already exists, '${outputFilePath}'`,
progressBarContainer,
});

return;
}

const fileBuffer = await fs.promises.readFile(filePath);
const fileBuffer = await fs.promises.readFile(filePath.input);
const processedFileBuffer = await processFunction({ fileBuffer, config });

await fs.promises.writeFile(preparedOutputFilePath, processedFileBuffer);
await fs.promises.mkdir(path.dirname(outputFilePath), { recursive: true });
await fs.promises.writeFile(outputFilePath, processedFileBuffer);

const fileSize = fileBuffer.length;
const processedFileSize = processedFileBuffer.length;
Expand All @@ -156,14 +148,14 @@ async function processFile({
const before = formatBytes(fileSize);
const after = formatBytes(processedFileSize);

logProgress(filePath, {
logProgress(filePath.input, {
type: LOG_TYPES.SUCCESS,
description: `${before}${format} ${after}. Ratio: ${ratio}%`,
progressBarContainer,
});
} catch (error) {
if (error.message) {
logProgress(filePath, {
logProgress(filePath.input, {
type: LOG_TYPES.ERROR,
description: (error.message || '').trim(),
progressBarContainer,
Expand Down
36 changes: 13 additions & 23 deletions lib/optimize.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,22 @@ import {
} from './log.js';
import { optionsToArguments } from './options-to-arguments.js';
import { parseImageMetadata } from './parse-image-metadata.js';
import prepareWriteFilePath from './prepare-write-file-path.js';
import { programOptions } from './program-options.js';
import { showTotal } from './show-total.js';

export default async function optimize({
inputFilePaths,
outputDirectoryPath,
config,
}) {
export default async function optimize({ filePaths, config }) {
const { isLossless } = programOptions;

const inputFilePathsCount = inputFilePaths.length;
const filePathsCount = filePaths.length;

if (inputFilePathsCount <= 0) {
if (filePathsCount <= 0) {
return;
}

log(`Optimizing ${inputFilePathsCount} ${getPlural(inputFilePathsCount, 'image', 'images')} (${isLossless ? 'lossless' : 'lossy'})...`);
log(`Optimizing ${filePathsCount} ${getPlural(filePathsCount, 'image', 'images')} (${isLossless ? 'lossless' : 'lossy'})...`);

const progressBarContainer = createProgressBarContainer(inputFilePathsCount);
const progressBar = progressBarContainer.create(inputFilePathsCount, 0);
const progressBarContainer = createProgressBarContainer(filePathsCount);
const progressBar = progressBarContainer.create(filePathsCount, 0);

const totalSize = { before: 0, after: 0 };

Expand All @@ -54,11 +49,10 @@ export default async function optimize({
*/
isLossless ? Math.round(cpuCount / 2) : cpuCount,
);
const tasksPromises = inputFilePaths.map(
const tasksPromises = filePaths.map(
filePath => tasksSimultaneousLimit(
() => processFile({
filePath,
outputDirectoryPath,
config,
progressBarContainer,
progressBar,
Expand All @@ -77,15 +71,14 @@ export default async function optimize({

async function processFile({
filePath,
outputDirectoryPath,
config,
progressBarContainer,
progressBar,
totalSize,
isLossless,
}) {
try {
const fileBuffer = await fs.promises.readFile(filePath);
const fileBuffer = await fs.promises.readFile(filePath.input);
const processedFileBuffer = await processFileByFormat({ fileBuffer, config, isLossless });

const fileSize = fileBuffer.length;
Expand All @@ -98,33 +91,30 @@ async function processFile({

const isOptimized = ratio > 0;
const isChanged = !fileBuffer.equals(processedFileBuffer);
const isSvg = path.extname(filePath).toLowerCase() === '.svg';
const isSvg = path.extname(filePath.input).toLowerCase() === '.svg';

if (!isOptimized && !(isChanged && isSvg)) {
logProgressVerbose(filePath, {
logProgressVerbose(filePath.input, {
description: `${(isChanged ? 'File size increased' : 'Nothing changed')}. Skipped`,
progressBarContainer,
});

return;
}

await fs.promises.writeFile(
prepareWriteFilePath(filePath, outputDirectoryPath),
processedFileBuffer,
);
await fs.promises.writeFile(filePath.output, processedFileBuffer);

const before = formatBytes(fileSize);
const after = formatBytes(processedFileSize);

logProgress(filePath, {
logProgress(filePath.input, {
type: isOptimized ? LOG_TYPES.SUCCESS : LOG_TYPES.WARNING,
description: `${before}${after}. Ratio: ${ratio}%`,
progressBarContainer,
});
} catch (error) {
if (error.message) {
logProgress(filePath, {
logProgress(filePath.input, {
type: LOG_TYPES.ERROR,
description: (error.message || '').trim(),
progressBarContainer,
Expand Down
73 changes: 73 additions & 0 deletions lib/prepare-file-paths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import fs from 'node:fs';
import path from 'node:path';

import { fdir } from 'fdir';

export async function prepareFilePaths({
inputPaths,
outputDirectoryPath,
extensions,
}) {
const files = new Set();
const directories = new Set();
const inputPathsSet = new Set(inputPaths);

await Promise.all(
[...inputPathsSet].map(async currentPath => {
try {
const stat = await fs.promises.stat(currentPath);

if (stat.isDirectory()) {
directories.add(currentPath);
} else if (stat.isFile() && checkFileType(currentPath, extensions)) {
files.add(getRelativePath(currentPath));
}
} catch {}
}),
);

const crawler = new fdir() // eslint-disable-line new-cap
.withFullPaths()
.filter(currentPath => checkFileType(currentPath, extensions));

const crawledPaths = await Promise.all(
[...directories].map(currentPath => crawler.crawl(currentPath).withPromise()),
);

for (const crawledPath of crawledPaths.flat()) {
files.add(crawledPath);
}

const result = [...files].map(filePath => {
let outputPath = filePath;

if (outputDirectoryPath) {
for (const directory of directories) {
if (filePath.startsWith(directory)) {
outputPath = path.join(outputDirectoryPath, filePath.slice(directory.length));
break;
}
}
}

return {
input: getRelativePath(filePath),
output: outputPath,
};
});

return result;
}

function getRelativePath(filePath) {
const replacePath = `${process.cwd()}${path.sep}`;

return filePath.startsWith(replacePath)
? filePath.slice(replacePath.length)
: filePath;
}

function checkFileType(filePath, extensions) {
const extension = path.extname(filePath).toLowerCase().slice(1);
return extensions.includes(extension);
}
53 changes: 0 additions & 53 deletions lib/prepare-input-file-paths.js

This file was deleted.

Loading

0 comments on commit 5f90ba9

Please sign in to comment.