Skip to content

Commit

Permalink
feat(cli): added directory option and wildcard for the output path
Browse files Browse the repository at this point in the history
  • Loading branch information
Guichaguri committed Apr 15, 2024
1 parent 9ade180 commit bc81955
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 10 deletions.
11 changes: 8 additions & 3 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ program
.version('1.2.0')
.showHelpAfterError()
.argument('<input>', 'input file path')
.argument('<output>', 'output file path')
.argument('<output>', 'output file path. use "@" to use the input file name')
.addOption(
new Option('-i, --input-format [format]', 'input file format')
.choices([
Expand Down Expand Up @@ -49,6 +49,10 @@ program
new Option('--output-options-file [path]', 'output options json file')
.default(null)
)
.addOption(
new Option('-d, --directory', 'whether the input and output paths are directories, in which case all files will be converted')
.default(false)
)
.action((inputFile, outputFile, opts) => {
runCli({
inputFile,
Expand All @@ -57,9 +61,10 @@ program
outputFormat: opts.outputFormat,
inputOptionsFile: opts.inputOptionsFile,
outputOptionsFile: opts.outputOptionsFile,
directory: opts.directory,
})
.then(() => console.log('Done.'))
.catch((error) => console.error(error));
});
.catch((error) => console.error(error?.toString()));
})

program.parse();
67 changes: 61 additions & 6 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createInput, getInputFormatAndOptions, InputFormat } from './input';
import { createOutput, getOutputFormatAndOptions, OutputFormat } from './output';
import { createOutput, getOutputFilename, getOutputFormatAndOptions, OutputFormat } from './output';
import * as fs from 'fs';
import * as fsPromises from 'fs/promises';
import * as path from 'path';

export interface CliOptions {
inputFile: string;
Expand All @@ -10,11 +11,28 @@ export interface CliOptions {
outputFormat: string;
inputOptionsFile?: string;
outputOptionsFile?: string;
directory?: boolean;
}

interface ConversionOptions {
inputFile: string;
outputFile: string;
inputFormat: InputFormat;
outputFormat: OutputFormat;
inputOptions: object;
outputOptions: object;
}

export async function runCli(options: CliOptions): Promise<void> {
console.log(`Converting "${options.inputFile}" to "${options.outputFile}"`);
const conversionOptions = await prepare(options);

if (options.directory)
await convertDirectory(conversionOptions);
else
await convert(conversionOptions);
}

export async function prepare(options: CliOptions): Promise<ConversionOptions> {
const [inFormat, defaultInOpts] = getInputFormatAndOptions(options.inputFormat, options.inputFile);
const [outFormat, defaultOutOpts] = getOutputFormatAndOptions(options.outputFormat, options.outputFile);

Expand All @@ -26,27 +44,64 @@ export async function runCli(options: CliOptions): Promise<void> {
const inputOptions = await loadOptionsFile(options.inputOptionsFile);
const outputOptions = await loadOptionsFile(options.outputOptionsFile);

const reader = createInput(inFormat, { ...defaultInOpts, ...inputOptions });
return {
inputFile: options.inputFile,
outputFile: options.outputFile,
inputFormat: inFormat,
outputFormat: outFormat,
inputOptions: { ...defaultInOpts, ...inputOptions },
outputOptions: { ...defaultOutOpts, ...outputOptions },
};
}

export async function convert(options: ConversionOptions): Promise<void> {
const reader = createInput(options.inputFormat, options.inputOptions);
const inputFileStream = fs.createReadStream(options.inputFile);

const readerStream = await reader.createStream(inputFileStream);
const channels = readerStream.channels;

const writer = createOutput(outFormat, { ...defaultOutOpts, ...outputOptions, channels });
const outputFileStream = fs.createWriteStream(options.outputFile);
const writer = createOutput(options.outputFormat, { ...options.outputOptions, channels });
const outputFile = getOutputFilename(options.outputFile, options.inputFile, writer);

await fsPromises.mkdir(path.dirname(outputFile));

const outputFileStream = fs.createWriteStream(outputFile);

const writerStream = writer.createStream(readerStream.stream);

writerStream.pipe(outputFileStream);

console.log('Converting...');
console.log(`Converting "${options.inputFile}" to "${outputFile}"`);

return new Promise((resolve, reject) => {
outputFileStream.once('finish', () => resolve());
outputFileStream.once('error', (err) => reject(err));
});
}

export async function convertDirectory(options: ConversionOptions): Promise<void> {
const inputDirents = await fsPromises.readdir(options.inputFile, { withFileTypes: true });
const inputFiles = inputDirents.filter(dirent => dirent.isFile());

if (inputFiles.length === 0)
console.log('No files found in the directory');

for (const file of inputFiles) {
console.log(`File: "${file.name}"`);

try {
await convert({
...options,
inputFile: path.join(options.inputFile, file.name),
outputFile: path.join(options.outputFile, '@'),
});
} catch (error) {
console.error(error?.toString());
}
}
}

async function loadOptionsFile(path: string | undefined): Promise<object> {
if (!path)
return {};
Expand Down
17 changes: 16 additions & 1 deletion src/cli/output.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as path from 'path';
import { BaseWriter } from '../interfaces/base.writer';
import {
CsvWriter,
Expand All @@ -12,7 +13,6 @@ import {
RacePakWriter,
WinDarabWriter,
} from '../index';
import { DataChannel } from '../interfaces/data-channel';

export enum OutputFormat {
AUTO = 'auto',
Expand Down Expand Up @@ -55,6 +55,21 @@ export function getOutputFormatAndOptions(format: string, filename: string): [Ou
throw new Error('Could not find an output format based on the file extension.');
}

export function getOutputFilename(outputFile: string, inputFile: string, writer: BaseWriter): string {
if (!outputFile.includes('@'))
return outputFile;

const suffix = outputFile.endsWith(writer.extension) ? '' : writer.extension;
const basename = path.basename(inputFile);
const outputName = outputFile.replace('@', basename + suffix);

// In case the output file is the same as the input, we'll prefix it with an underline
if (outputName === inputFile)
return outputFile.replace('@', '_' + basename + suffix);

return outputName;
}

export function createOutput(format: OutputFormat, options: any): BaseWriter {
if (format === OutputFormat.CSV)
return new CsvWriter(options);
Expand Down

0 comments on commit bc81955

Please sign in to comment.