Skip to content

Commit c8ffc73

Browse files
authored
Merge pull request #25 from sonicbaume/file-adapter
File IO using adapter
2 parents 880dcef + 5fb39f3 commit c8ffc73

46 files changed

Lines changed: 866 additions & 918 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,6 @@ examples/gemini_response.txt
186186
aac-metrics/
187187
compare-effort-scores.ts
188188
.DS_Store
189+
190+
# Watchman config (for dev env)
191+
.watchmanconfig

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cli/index.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,15 @@ import {
1010
} from '../utilities/analytics/history';
1111
import { ComparisonAnalyzer, MetricsCalculator } from '../utilities/analytics';
1212
import { CellScanningOrder, ScanningSelectionMethod } from '../types/aac';
13-
import path from 'path';
14-
import fs from 'fs';
13+
import { defaultFileAdapter, extname } from '../utils/io';
14+
15+
const { pathExists, isDirectory, join, readTextFromInput, basename, writeTextToPath } =
16+
defaultFileAdapter;
1517

1618
// Helper function to detect format from file/folder path
1719
function detectFormat(filePath: string): string {
1820
// Check if it's a folder ending with .ascconfig
19-
if (
20-
fs.existsSync(filePath) &&
21-
fs.statSync(filePath).isDirectory() &&
22-
filePath.endsWith('.ascconfig')
23-
) {
21+
if (pathExists(filePath) && isDirectory(filePath) && filePath.endsWith('.ascconfig')) {
2422
return 'ascconfig';
2523
}
2624

@@ -33,7 +31,7 @@ function detectFormat(filePath: string): string {
3331
}
3432

3533
// Otherwise use file extension
36-
return path.extname(filePath).slice(1);
34+
return extname(filePath).slice(1);
3735
}
3836

3937
// Helper function to parse filtering options from CLI arguments
@@ -86,9 +84,9 @@ function parseFilteringOptions(options: {
8684
}
8785

8886
// Set version from package.json
89-
const packageJson = JSON.parse(
90-
fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8')
91-
) as { version: string };
87+
const packageJson = JSON.parse(readTextFromInput(join(__dirname, '../../package.json'))) as {
88+
version: string;
89+
};
9290
program.version(packageJson.version);
9391

9492
program
@@ -403,14 +401,13 @@ program
403401
}
404402
) => {
405403
try {
406-
if (!fs.existsSync(input)) {
404+
if (!pathExists(input)) {
407405
throw new Error(`File not found: ${input}`);
408406
}
409407

410408
const normalizedSource = (options.source || 'auto').toLowerCase();
411-
const ext = path.extname(input).toLowerCase();
412-
const isGrid3Db =
413-
ext === '.sqlite' || path.basename(input).toLowerCase() === 'history.sqlite';
409+
const ext = extname(input).toLowerCase();
410+
const isGrid3Db = ext === '.sqlite' || basename(input).toLowerCase() === 'history.sqlite';
414411
const isSnap = ext === '.sps' || ext === '.spb';
415412

416413
let entries;
@@ -438,7 +435,7 @@ program
438435

439436
const output = JSON.stringify(payload, null, 2);
440437
if (options.out) {
441-
fs.writeFileSync(options.out, output);
438+
writeTextToPath(options.out, output);
442439
} else {
443440
console.log(output);
444441
}
@@ -578,7 +575,7 @@ program
578575

579576
const output = options.pretty ? JSON.stringify(result, null, 2) : JSON.stringify(result);
580577
if (options.out) {
581-
fs.writeFileSync(options.out, output);
578+
writeTextToPath(options.out, output);
582579
} else {
583580
console.log(output);
584581
}

src/core/baseProcessor.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import { AACTree, AACButton, AACSemanticCategory } from './treeStructure';
4444
import { StringCasing, detectCasing, isNumericOrEmpty } from './stringCasing';
4545
import { ValidationResult } from '../validation/validationTypes';
46-
import { BinaryOutput, ProcessorInput } from '../utils/io';
46+
import { BinaryOutput, defaultFileAdapter, FileAdapter, ProcessorInput } from '../utils/io';
4747
import { getZipAdapter, ZipAdapter } from '../utils/zip';
4848

4949
// Configuration options for processors
@@ -73,6 +73,9 @@ export interface ProcessorConfig {
7373
// Defaults to 'en-GB' if not specified
7474
grid3Locale?: string;
7575

76+
// Adapter for reading/writing files
77+
fileAdapter: FileAdapter;
78+
7679
// Adapter for handling encoding/decoding zip files
7780
zipAdapter: (input?: ProcessorInput) => Promise<ZipAdapter>;
7881
}
@@ -126,6 +129,7 @@ abstract class BaseProcessor {
126129
excludeNavigationButtons: true,
127130
excludeSystemButtons: true,
128131
preserveAllButtons: false,
132+
fileAdapter: defaultFileAdapter,
129133
zipAdapter: getZipAdapter,
130134
...options,
131135
};

src/core/fileProcessor.ts

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/processors/applePanelsProcessor.ts

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,12 @@ import {
1313
AACSemanticCategory,
1414
AACSemanticIntent,
1515
} from '../core/treeStructure';
16-
// Removed unused import: FileProcessor
1716
import plist, { PlistValue } from 'plist';
1817
import {
1918
ValidationFailureError,
2019
buildValidationResultFromMessage,
2120
} from '../validation/validationTypes';
22-
import {
23-
ProcessorInput,
24-
getBasename,
25-
getFs,
26-
getPath,
27-
readBinaryFromInput,
28-
readTextFromInput,
29-
writeTextToPath,
30-
} from '../utils/io';
21+
import { ProcessorInput, getBasename } from '../utils/io';
3122

3223
interface ApplePanelsActionParameters {
3324
CharString?: string;
@@ -227,24 +218,24 @@ class ApplePanelsProcessor extends BaseProcessor {
227218
}
228219

229220
async loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree> {
221+
const { readBinaryFromInput, readTextFromInput, pathExists, getFileSize, join } =
222+
this.options.fileAdapter;
230223
await Promise.resolve();
231224
const filename =
232225
typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.plist';
233226
let buffer: Uint8Array;
234227

235228
try {
236229
if (typeof filePathOrBuffer === 'string') {
237-
const fs = getFs();
238-
const path = getPath();
239230
if (filePathOrBuffer.endsWith('.ascconfig')) {
240-
const panelDefsPath = path.join(
231+
const panelDefsPath = join(
241232
filePathOrBuffer,
242233
'Contents',
243234
'Resources',
244235
'PanelDefinitions.plist'
245236
);
246-
if (fs.existsSync(panelDefsPath)) {
247-
buffer = fs.readFileSync(panelDefsPath);
237+
if (pathExists(panelDefsPath)) {
238+
buffer = readBinaryFromInput(panelDefsPath);
248239
} else {
249240
const validation = buildValidationResultFromMessage({
250241
filename,
@@ -257,7 +248,7 @@ class ApplePanelsProcessor extends BaseProcessor {
257248
throw new ValidationFailureError('Apple Panels file not found', validation);
258249
}
259250
} else {
260-
buffer = fs.readFileSync(filePathOrBuffer);
251+
buffer = readBinaryFromInput(filePathOrBuffer);
261252
}
262253
} else {
263254
buffer = readBinaryFromInput(filePathOrBuffer);
@@ -403,8 +394,7 @@ class ApplePanelsProcessor extends BaseProcessor {
403394
filesize:
404395
typeof filePathOrBuffer === 'string'
405396
? (() => {
406-
const fs = getFs();
407-
return fs.existsSync(filePathOrBuffer) ? fs.statSync(filePathOrBuffer).size : 0;
397+
return pathExists(filePathOrBuffer) ? getFileSize(filePathOrBuffer) : 0;
408398
})()
409399
: readBinaryFromInput(filePathOrBuffer).byteLength,
410400
format: 'applepanels',
@@ -421,6 +411,7 @@ class ApplePanelsProcessor extends BaseProcessor {
421411
translations: Map<string, string>,
422412
outputPath: string
423413
): Promise<Uint8Array> {
414+
const { readBinaryFromInput, join } = this.options.fileAdapter;
424415
// Load the tree, apply translations, and save to new file
425416
const tree = await this.loadIntoTree(filePathOrBuffer);
426417

@@ -477,13 +468,13 @@ class ApplePanelsProcessor extends BaseProcessor {
477468
if (outputPath.endsWith('.plist')) {
478469
return readBinaryFromInput(outputPath);
479470
}
480-
const path = getPath();
481471
const configPath = outputPath.endsWith('.ascconfig') ? outputPath : `${outputPath}.ascconfig`;
482-
const panelDefsPath = path.join(configPath, 'Contents', 'Resources', 'PanelDefinitions.plist');
472+
const panelDefsPath = join(configPath, 'Contents', 'Resources', 'PanelDefinitions.plist');
483473
return readBinaryFromInput(panelDefsPath);
484474
}
485475

486476
async saveFromTree(tree: AACTree, outputPath: string): Promise<void> {
477+
const { writeTextToPath, pathExists, mkDir, join, dirname } = this.options.fileAdapter;
487478
await Promise.resolve();
488479
// Support two output modes:
489480
// 1) Single-file .plist (PanelDefinitions.plist content written directly)
@@ -495,15 +486,13 @@ class ApplePanelsProcessor extends BaseProcessor {
495486
let contentsPath = '';
496487
let resourcesPath = '';
497488
if (!isSinglePlist) {
498-
const fs = getFs();
499-
const path = getPath();
500489
configPath = outputPath.endsWith('.ascconfig') ? outputPath : `${outputPath}.ascconfig`;
501-
contentsPath = path.join(configPath, 'Contents');
502-
resourcesPath = path.join(contentsPath, 'Resources');
490+
contentsPath = join(configPath, 'Contents');
491+
resourcesPath = join(contentsPath, 'Resources');
503492

504-
if (!fs.existsSync(configPath)) fs.mkdirSync(configPath, { recursive: true });
505-
if (!fs.existsSync(contentsPath)) fs.mkdirSync(contentsPath, { recursive: true });
506-
if (!fs.existsSync(resourcesPath)) fs.mkdirSync(resourcesPath, { recursive: true });
493+
if (!pathExists(configPath)) mkDir(configPath, { recursive: true });
494+
if (!pathExists(contentsPath)) mkDir(contentsPath, { recursive: true });
495+
if (!pathExists(resourcesPath)) mkDir(resourcesPath, { recursive: true });
507496

508497
// Create Info.plist (bundle mode only)
509498
const infoPlist = {
@@ -521,11 +510,11 @@ class ApplePanelsProcessor extends BaseProcessor {
521510
`Generated by AAC Processors${tree.metadata?.author ? ` - Author: ${tree.metadata.author}` : ''}`,
522511
};
523512
const infoPlistContent = plist.build(infoPlist);
524-
writeTextToPath(path.join(contentsPath, 'Info.plist'), infoPlistContent);
513+
writeTextToPath(join(contentsPath, 'Info.plist'), infoPlistContent);
525514

526515
// Create AssetIndex.plist (empty)
527516
const assetIndexContent = plist.build({});
528-
writeTextToPath(path.join(resourcesPath, 'AssetIndex.plist'), assetIndexContent);
517+
writeTextToPath(join(resourcesPath, 'AssetIndex.plist'), assetIndexContent);
529518
}
530519

531520
// Build PanelDefinitions content from tree
@@ -666,15 +655,12 @@ class ApplePanelsProcessor extends BaseProcessor {
666655

667656
if (isSinglePlist) {
668657
// Write single PanelDefinitions.plist file directly
669-
const fs = getFs();
670-
const path = getPath();
671-
const dir = path.dirname(outputPath);
672-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
658+
const dir = dirname(outputPath);
659+
if (!pathExists(dir)) mkDir(dir, { recursive: true });
673660
writeTextToPath(outputPath, panelDefsContent);
674661
} else {
675662
// Write into bundle structure
676-
const path = getPath();
677-
writeTextToPath(path.join(resourcesPath, 'PanelDefinitions.plist'), panelDefsContent);
663+
writeTextToPath(join(resourcesPath, 'PanelDefinitions.plist'), panelDefsContent);
678664
}
679665
}
680666

0 commit comments

Comments
 (0)