Skip to content

Commit

Permalink
Chore: increase code quality (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm authored Dec 4, 2022
1 parent 436d34d commit d44bbbd
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 295 deletions.
17 changes: 16 additions & 1 deletion src/types/files/file.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import crc32 from 'crc/crc32';
import fs, { PathLike } from 'fs';
import fs, { OpenMode, PathLike } from 'fs';
import path from 'path';
import { Readable } from 'stream';
import util from 'util';

import Constants from '../../constants.js';
import FilePoly from '../../polyfill/filePoly.js';
import fsPoly from '../../polyfill/fsPoly.js';
import Patch from '../patches/patch.js';
import FileHeader from './fileHeader.js';
Expand Down Expand Up @@ -144,6 +145,20 @@ export default class File {
return callback(this.getFilePath());
}

async extractToFilePoly<T>(
flags: OpenMode,
callback: (filePoly: FilePoly) => (T | Promise<T>),
): Promise<T> {
return this.extractToFile(async (localFile) => {
const filePoly = await FilePoly.fileFrom(localFile, flags);
try {
return await callback(filePoly);
} finally {
await filePoly.close();
}
});
}

async extractToTempFile<T>(
callback: (localFile: string) => (T | Promise<T>),
): Promise<T> {
Expand Down
175 changes: 104 additions & 71 deletions src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,19 +259,19 @@ export default class Options implements OptionsProps {
}

async scanDatFiles(): Promise<string[]> {
return Options.scanPath(this.dat);
return Options.scanPaths(this.dat);
}

getInputFileCount(): number {
return this.input.length;
}

private async scanInputFiles(): Promise<string[]> {
return Options.scanPath(this.input);
return Options.scanPaths(this.input);
}

private async scanInputExcludeFiles(): Promise<string[]> {
return Options.scanPath(this.inputExclude);
return Options.scanPaths(this.inputExclude);
}

async scanInputFilesWithoutExclusions(): Promise<string[]> {
Expand All @@ -286,44 +286,14 @@ export default class Options implements OptionsProps {
}

async scanPatchFiles(): Promise<string[]> {
return Options.scanPath(this.patch);
return Options.scanPaths(this.patch);
}

private static async scanPath(inputPaths: string[]): Promise<string[]> {
private static async scanPaths(inputPaths: string[]): Promise<string[]> {
const globbedPaths = (await Promise.all(inputPaths
.filter((inputPath) => inputPath)
.map(async (inputPath) => {
// Windows will report that \\.\nul doesn't exist, catch it explicitly
if (inputPath === os.devNull || inputPath.startsWith(os.devNull + path.sep)) {
return [];
}

// fg only uses forward-slash path separators
const inputPathNormalized = inputPath.replace(/\\/g, '/');

// Glob the contents of directories
if (await fsPoly.isDirectory(inputPath)) {
const dirPaths = (await fg(`${fg.escapePath(inputPathNormalized)}/**`))
.map((filePath) => path.normalize(filePath));
if (!dirPaths || !dirPaths.length) {
throw new Error(`${inputPath}: Path doesn't exist`);
}
return dirPaths;
}

// If the file exists, don't process it as a glob pattern
if (await fsPoly.exists(inputPath)) {
return [inputPath];
}

// Otherwise, process it as a glob pattern
const paths = (await fg(inputPathNormalized))
.map((filePath) => path.normalize(filePath));
if (!paths || !paths.length) {
throw new Error(`${inputPath}: Path doesn't exist`);
}
return paths;
}))).flatMap((paths) => paths);
.map(this.globPath)))
.flatMap((paths) => paths);

// Filter to files
const isFiles = await Promise.all(
Expand All @@ -338,6 +308,39 @@ export default class Options implements OptionsProps {
.filter((inputPath, idx, arr) => arr.indexOf(inputPath) === idx);
}

private static async globPath(inputPath: string): Promise<string[]> {
// Windows will report that \\.\nul doesn't exist, catch it explicitly
if (inputPath === os.devNull || inputPath.startsWith(os.devNull + path.sep)) {
return [];
}

// fg only uses forward-slash path separators
const inputPathNormalized = inputPath.replace(/\\/g, '/');

// Glob the contents of directories
if (await fsPoly.isDirectory(inputPath)) {
const dirPaths = (await fg(`${fg.escapePath(inputPathNormalized)}/**`))
.map((filePath) => path.normalize(filePath));
if (!dirPaths || !dirPaths.length) {
throw new Error(`${inputPath}: Path doesn't exist`);
}
return dirPaths;
}

// If the file exists, don't process it as a glob pattern
if (await fsPoly.exists(inputPath)) {
return [inputPath];
}

// Otherwise, process it as a glob pattern
const paths = (await fg(inputPathNormalized))
.map((filePath) => path.normalize(filePath));
if (!paths || !paths.length) {
throw new Error(`${inputPath}: Path doesn't exist`);
}
return paths;
}

private getOutput(): string {
return this.shouldWrite() ? this.output : Constants.GLOBAL_TEMP_DIR;
}
Expand Down Expand Up @@ -368,7 +371,13 @@ export default class Options implements OptionsProps {
const romFilenameSanitized = romFilename?.replace(/[\\/]/g, '_');

let output = this.getOutput();
output = Options.replaceOutputTokens(output, dat, inputRomPath, release, romFilenameSanitized);
output = Options.replaceTokensInOutputPath(
output,
dat,
inputRomPath,
release,
romFilenameSanitized,
);

return fsPoly.makeLegal(output);
}
Expand Down Expand Up @@ -422,35 +431,18 @@ export default class Options implements OptionsProps {
return fsPoly.makeLegal(output);
}

private static replaceOutputTokens(
output: string,
private static replaceTokensInOutputPath(
outputPath: string,
dat?: DAT,
inputRomPath?: string,
release?: Release,
outputRomFilename?: string,
): string {
let result = output;
if (dat) {
result = result.replace('{datName}', dat.getName().replace(/[\\/]/g, '_'));
}
if (release) {
result = result.replace('{datReleaseRegion}', release.getRegion());
if (release.getLanguage()) {
result = result.replace('{datReleaseLanguage}', release.getLanguage() as string);
}
}
if (inputRomPath) {
const inputRom = path.parse(inputRomPath);
result = result
.replace('{inputDirname}', inputRom.dir);
}
if (outputRomFilename) {
const outputRom = path.parse(outputRomFilename);
result = result
.replace('{outputBasename}', outputRom.base)
.replace('{outputName}', outputRom.name)
.replace('{outputExt}', outputRom.ext.replace(/^\./, ''));
}
let result = outputPath;
result = this.replaceDatTokens(result, dat);
result = this.replaceReleaseTokens(result, release);
result = this.replaceInputTokens(result, inputRomPath);
result = this.replaceOutputTokens(result, outputRomFilename);
result = this.replaceOutputGameConsoleTokens(result, dat, outputRomFilename);

const leftoverTokens = result.match(/\{[a-zA-Z]+\}/g);
Expand All @@ -461,29 +453,70 @@ export default class Options implements OptionsProps {
return result;
}

private static replaceDatTokens(input: string, dat?: DAT): string {
if (!dat) {
return input;
}

return input.replace('{datName}', dat.getName().replace(/[\\/]/g, '_'));
}

private static replaceReleaseTokens(input: string, release?: Release): string {
if (!release) {
return input;
}

let output = input;
output = output.replace('{datReleaseRegion}', release.getRegion());
if (release.getLanguage()) {
output = output.replace('{datReleaseLanguage}', release.getLanguage() as string);
}
return output;
}

private static replaceInputTokens(input: string, inputRomPath?: string): string {
if (!inputRomPath) {
return input;
}

return input.replace('{inputDirname}', path.parse(inputRomPath).dir);
}

private static replaceOutputTokens(input: string, outputRomFilename?: string): string {
if (!outputRomFilename) {
return input;
}

const outputRom = path.parse(outputRomFilename);
return input
.replace('{outputBasename}', outputRom.base)
.replace('{outputName}', outputRom.name)
.replace('{outputExt}', outputRom.ext.replace(/^\./, ''));
}

private static replaceOutputGameConsoleTokens(
output: string,
input: string,
dat?: DAT,
outputRomFilename?: string,
): string {
if (!outputRomFilename) {
return output;
return input;
}

const gameConsole = GameConsole.getForFilename(outputRomFilename)
|| GameConsole.getForConsoleName(dat?.getName() || '');
if (!gameConsole) {
return output;
return input;
}

let result = output;
let output = input;
if (gameConsole.getPocket()) {
result = result.replace('{pocket}', gameConsole.getPocket() as string);
output = output.replace('{pocket}', gameConsole.getPocket() as string);
}
if (gameConsole.getMister()) {
result = result.replace('{mister}', gameConsole.getMister() as string);
output = output.replace('{mister}', gameConsole.getMister() as string);
}
return result;
return output;
}

getOutputReportPath(): string {
Expand Down Expand Up @@ -562,7 +595,7 @@ export default class Options implements OptionsProps {
}

private async scanCleanExcludeFiles(): Promise<string[]> {
return Options.scanPath(this.cleanExclude);
return Options.scanPaths(this.cleanExclude);
}

async scanOutputFilesWithoutCleanExclusions(
Expand All @@ -577,7 +610,7 @@ export default class Options implements OptionsProps {
const cleanExcludedFilesNormalized = (await this.scanCleanExcludeFiles())
.map((filePath) => path.normalize(filePath));

return (await Options.scanPath(outputDirs))
return (await Options.scanPaths(outputDirs))
.map((filePath) => path.normalize(filePath))
.filter((filePath) => writtenFilesNormalized.indexOf(filePath) === -1)
.filter((filePath) => cleanExcludedFilesNormalized.indexOf(filePath) === -1);
Expand Down
Loading

0 comments on commit d44bbbd

Please sign in to comment.