@ecopages/file-system 0.2.0-alpha.5 → 0.2.0-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -22
- package/package.json +1 -4
- package/src/adapters/node.d.ts +1 -1
- package/src/adapters/node.js +13 -6
- package/src/index.d.ts +1 -1
- package/CHANGELOG.md +0 -11
- package/src/adapters/bun.ts +0 -83
- package/src/adapters/node.ts +0 -77
- package/src/index.ts +0 -42
- package/src/types.ts +0 -187
- package/src/utils/common.ts +0 -223
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# @ecopages/file-system
|
|
2
2
|
|
|
3
|
-
Runtime-agnostic file system utilities for Ecopages
|
|
3
|
+
Runtime-agnostic file system utilities for Ecopages that automatically select the optimal adapter (Bun or Node.js) based on the execution environment.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Runtime Detection**: Automatically
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **Type Safe**: Full TypeScript support with consistent interface
|
|
7
|
+
- **Runtime Detection**: Automatically uses `Bun.Glob`, `Bun.hash`, `Bun.file` for maximum performance on Bun, and falls back to native `node:fs/promises.glob()` and `crypto` on Node.js.
|
|
8
|
+
- **Unified Interface**: Write file system code once; let the runtime handle the optimization.
|
|
9
|
+
- **Type Safe**: Full TypeScript support with a consistent API.
|
|
11
10
|
|
|
12
11
|
## Installation
|
|
13
12
|
|
|
@@ -41,11 +40,11 @@ if (fileSystem.exists('file.txt')) {
|
|
|
41
40
|
## API
|
|
42
41
|
|
|
43
42
|
| Method | Description |
|
|
44
|
-
|
|
|
43
|
+
| :-------------------------- | :-------------------------------- |
|
|
45
44
|
| `glob(patterns, options)` | Find files matching glob patterns |
|
|
46
45
|
| `readFile(path)` | Read file as string (async) |
|
|
47
46
|
| `readFileSync(path)` | Read file as string (sync) |
|
|
48
|
-
| `readFileAsBuffer(path)` | Read file as Buffer
|
|
47
|
+
| `readFileAsBuffer(path)` | Read file as `Buffer` |
|
|
49
48
|
| `write(path, content)` | Write content to file |
|
|
50
49
|
| `writeAsync(path, content)` | Write content to file (async) |
|
|
51
50
|
| `exists(path)` | Check if path exists |
|
|
@@ -59,18 +58,3 @@ if (fileSystem.exists('file.txt')) {
|
|
|
59
58
|
| `gzipDir(path, extensions)` | Gzip files in directory |
|
|
60
59
|
| `isDirectory(path)` | Check if path is directory |
|
|
61
60
|
| `verifyFileExists(path)` | Throw if file doesn't exist |
|
|
62
|
-
|
|
63
|
-
## Performance
|
|
64
|
-
|
|
65
|
-
Benchmark results (Apple M4):
|
|
66
|
-
|
|
67
|
-
| Operation | BunFileSystem | NodeFileSystem |
|
|
68
|
-
| ---------------- | ------------- | -------------- |
|
|
69
|
-
| glob (100 files) | 64.85 µs | 71.06 µs |
|
|
70
|
-
| hash (1MB file) | **87 µs** | **393 µs** |
|
|
71
|
-
|
|
72
|
-
Bun adapter is **4.5x faster** for file hashing.
|
|
73
|
-
|
|
74
|
-
## License
|
|
75
|
-
|
|
76
|
-
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ecopages/file-system",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.50",
|
|
4
4
|
"description": "Runtime-agnostic file system utilities for Ecopages with Bun and Node adapters",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ecopages",
|
|
@@ -44,9 +44,6 @@
|
|
|
44
44
|
"default": "./src/adapters/node.js"
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"fast-glob": "^3.3.2"
|
|
49
|
-
},
|
|
50
47
|
"peerDependencies": {
|
|
51
48
|
"bun": ">=1.0.0"
|
|
52
49
|
},
|
package/src/adapters/node.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module @ecopages/file-system/adapters/node
|
|
3
|
-
* @description Node.js file system adapter using
|
|
3
|
+
* @description Node.js file system adapter using node:fs/promises.glob(), crypto, and node:fs.
|
|
4
4
|
*/
|
|
5
5
|
import type { FileSystem, GlobOptions } from '../types.js';
|
|
6
6
|
import { BaseFileSystem } from '../utils/common.js';
|
package/src/adapters/node.js
CHANGED
|
@@ -2,12 +2,22 @@ import crypto from "node:crypto";
|
|
|
2
2
|
import {
|
|
3
3
|
access as accessAsync,
|
|
4
4
|
cp as cpAsync,
|
|
5
|
+
glob as globAsync,
|
|
5
6
|
readFile as readFileAsync,
|
|
6
7
|
writeFile as writeFileAsync
|
|
7
8
|
} from "node:fs/promises";
|
|
8
9
|
import { dirname } from "node:path";
|
|
9
|
-
import fg from "fast-glob";
|
|
10
10
|
import { BaseFileSystem } from "../utils/common.js";
|
|
11
|
+
async function collectGlobMatches(pattern, options) {
|
|
12
|
+
const matches = [];
|
|
13
|
+
for await (const entry of globAsync(pattern, {
|
|
14
|
+
cwd: options.cwd ?? process.cwd(),
|
|
15
|
+
exclude: options.ignore
|
|
16
|
+
})) {
|
|
17
|
+
matches.push(entry);
|
|
18
|
+
}
|
|
19
|
+
return matches;
|
|
20
|
+
}
|
|
11
21
|
class NodeFileSystem extends BaseFileSystem {
|
|
12
22
|
async existsAsync(filePath) {
|
|
13
23
|
try {
|
|
@@ -39,11 +49,8 @@ class NodeFileSystem extends BaseFileSystem {
|
|
|
39
49
|
await cpAsync(source, destination);
|
|
40
50
|
}
|
|
41
51
|
async glob(patterns, options = {}) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ignore: options.ignore,
|
|
45
|
-
...options
|
|
46
|
-
});
|
|
52
|
+
const files = await Promise.all(patterns.map((pattern) => collectGlobMatches(pattern, options)));
|
|
53
|
+
return Array.from(new Set(files.flat()));
|
|
47
54
|
}
|
|
48
55
|
hash(path) {
|
|
49
56
|
try {
|
package/src/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Automatically selects the optimal adapter based on runtime:
|
|
6
6
|
* - **Bun**: Uses `Bun.Glob`, `Bun.hash`, `Bun.file` for maximum performance
|
|
7
|
-
* - **Node.js**: Uses `
|
|
7
|
+
* - **Node.js**: Uses native `node:fs/promises.glob()` and `crypto`
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```typescript
|
package/CHANGELOG.md
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@ecopages/file-system` are documented here.
|
|
4
|
-
|
|
5
|
-
> **Note:** Changelog tracking begins at version `0.2.0`. Changes prior to this release are not recorded here but are available in the git history.
|
|
6
|
-
|
|
7
|
-
## [UNRELEASED] — TBD
|
|
8
|
-
|
|
9
|
-
### Refactoring
|
|
10
|
-
|
|
11
|
-
- `package.json` updated to add `fs` as an explicit peer dependency, reflecting the package's role in the runtime-agnostic file abstraction layer used by the Node adapter.
|
package/src/adapters/bun.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @ecopages/file-system/adapters/bun
|
|
3
|
-
* @description Bun-optimized file system adapter using Bun.Glob, Bun.hash, and Bun.file.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { dirname } from 'node:path';
|
|
7
|
-
import type { GlobScanOptions } from 'bun';
|
|
8
|
-
import type { FileSystem, GlobOptions } from '../types.ts';
|
|
9
|
-
import { BaseFileSystem } from '../utils/common.ts';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Bun-optimized implementation of the FileSystem interface.
|
|
13
|
-
*/
|
|
14
|
-
export class BunFileSystem extends BaseFileSystem implements FileSystem {
|
|
15
|
-
async existsAsync(path: string): Promise<boolean> {
|
|
16
|
-
return Bun.file(path).exists();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async readFile(path: string): Promise<string> {
|
|
20
|
-
try {
|
|
21
|
-
this.verifyFileExists(path);
|
|
22
|
-
return await Bun.file(path).text();
|
|
23
|
-
} catch (error) {
|
|
24
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
-
throw new Error(`Error reading file: ${path}, ${message}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async writeAsync(filepath: string, contents: string | Buffer): Promise<void> {
|
|
30
|
-
try {
|
|
31
|
-
await this.ensureDirAsync(dirname(filepath));
|
|
32
|
-
await Bun.write(filepath, contents);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
-
throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async copyFileAsync(source: string, destination: string): Promise<void> {
|
|
40
|
-
try {
|
|
41
|
-
await Bun.write(destination, Bun.file(source));
|
|
42
|
-
} catch (error) {
|
|
43
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
-
throw new Error(`Error copying file: ${source} to ${destination}. Cause: ${message}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async glob(patterns: string[], options: GlobOptions = {}): Promise<string[]> {
|
|
49
|
-
const scanOptions: GlobScanOptions = {
|
|
50
|
-
cwd: options.cwd ?? process.cwd(),
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const promises = patterns.map((pattern) => {
|
|
54
|
-
const glob = new Bun.Glob(pattern);
|
|
55
|
-
return Array.fromAsync(glob.scan(scanOptions));
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const results = await Promise.all(promises);
|
|
59
|
-
let files = results.flat();
|
|
60
|
-
|
|
61
|
-
if (options.ignore?.length) {
|
|
62
|
-
const ignoreGlobs = options.ignore.map((pattern) => new Bun.Glob(pattern));
|
|
63
|
-
files = files.filter((file) => !ignoreGlobs.some((glob) => glob.match(file)));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return files;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
hash(path: string): string {
|
|
70
|
-
try {
|
|
71
|
-
const buffer = this.readFileAsBuffer(path);
|
|
72
|
-
return Bun.hash(buffer).toString();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
-
throw new Error(`Error hashing file: ${path}. Cause: ${message}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Singleton instance for Bun runtime.
|
|
82
|
-
*/
|
|
83
|
-
export const bunFs: BunFileSystem = new BunFileSystem();
|
package/src/adapters/node.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @ecopages/file-system/adapters/node
|
|
3
|
-
* @description Node.js file system adapter using fast-glob, crypto, and node:fs.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import crypto from 'node:crypto';
|
|
7
|
-
import {
|
|
8
|
-
access as accessAsync,
|
|
9
|
-
cp as cpAsync,
|
|
10
|
-
readFile as readFileAsync,
|
|
11
|
-
writeFile as writeFileAsync,
|
|
12
|
-
} from 'node:fs/promises';
|
|
13
|
-
import { dirname } from 'node:path';
|
|
14
|
-
import fg from 'fast-glob';
|
|
15
|
-
import type { FileSystem, GlobOptions } from '../types.ts';
|
|
16
|
-
import { BaseFileSystem } from '../utils/common.ts';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Node.js implementation of the FileSystem interface.
|
|
20
|
-
*/
|
|
21
|
-
export class NodeFileSystem extends BaseFileSystem implements FileSystem {
|
|
22
|
-
async existsAsync(filePath: string): Promise<boolean> {
|
|
23
|
-
try {
|
|
24
|
-
await accessAsync(filePath);
|
|
25
|
-
return true;
|
|
26
|
-
} catch {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async readFile(path: string): Promise<string> {
|
|
32
|
-
try {
|
|
33
|
-
this.verifyFileExists(path);
|
|
34
|
-
return await readFileAsync(path, 'utf-8');
|
|
35
|
-
} catch (error) {
|
|
36
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
-
throw new Error(`Error reading file: ${path}, ${message}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async writeAsync(filepath: string, contents: string | Buffer): Promise<void> {
|
|
42
|
-
try {
|
|
43
|
-
await this.ensureDirAsync(dirname(filepath));
|
|
44
|
-
await writeFileAsync(filepath, contents);
|
|
45
|
-
} catch (error) {
|
|
46
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
-
throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async copyFileAsync(source: string, destination: string): Promise<void> {
|
|
52
|
-
await cpAsync(source, destination);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async glob(patterns: string[], options: GlobOptions = {}): Promise<string[]> {
|
|
56
|
-
return fg(patterns, {
|
|
57
|
-
cwd: options.cwd ?? process.cwd(),
|
|
58
|
-
ignore: options.ignore,
|
|
59
|
-
...options,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
hash(path: string): string {
|
|
64
|
-
try {
|
|
65
|
-
const buffer = this.readFileAsBuffer(path);
|
|
66
|
-
return crypto.createHash('sha256').update(buffer).digest('hex');
|
|
67
|
-
} catch (error) {
|
|
68
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
-
throw new Error(`Error hashing file: ${path}. Cause: ${message}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Singleton instance for Node.js runtime.
|
|
76
|
-
*/
|
|
77
|
-
export const nodeFs: NodeFileSystem = new NodeFileSystem();
|
package/src/index.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @ecopages/file-system
|
|
3
|
-
* @description Runtime-agnostic file system utilities for Ecopages.
|
|
4
|
-
*
|
|
5
|
-
* Automatically selects the optimal adapter based on runtime:
|
|
6
|
-
* - **Bun**: Uses `Bun.Glob`, `Bun.hash`, `Bun.file` for maximum performance
|
|
7
|
-
* - **Node.js**: Uses `fast-glob` and `crypto` for compatibility
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* import { fileSystem } from '@ecopages/file-system';
|
|
12
|
-
*
|
|
13
|
-
* const files = await fileSystem.glob(['**\/*.ts']);
|
|
14
|
-
* const content = await fileSystem.readFile('file.txt');
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
export * from './types.ts';
|
|
18
|
-
export { BaseFileSystem } from './utils/common.ts';
|
|
19
|
-
export { BunFileSystem, bunFs } from './adapters/bun.ts';
|
|
20
|
-
export { NodeFileSystem, nodeFs } from './adapters/node.ts';
|
|
21
|
-
|
|
22
|
-
import type { FileSystem } from './types.ts';
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Creates a FileSystem instance based on the current runtime.
|
|
26
|
-
* Uses Bun adapter if Bun is available, otherwise Node adapter.
|
|
27
|
-
*/
|
|
28
|
-
async function createFileSystem(): Promise<FileSystem> {
|
|
29
|
-
if (typeof Bun !== 'undefined') {
|
|
30
|
-
const { bunFs } = await import('./adapters/bun.ts');
|
|
31
|
-
return bunFs;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { nodeFs } = await import('./adapters/node.ts');
|
|
35
|
-
return nodeFs;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Runtime-agnostic file system instance.
|
|
40
|
-
* Automatically uses Bun or Node adapter based on environment.
|
|
41
|
-
*/
|
|
42
|
-
export const fileSystem: FileSystem = await createFileSystem();
|
package/src/types.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @ecopages/file-system/types
|
|
3
|
-
* @description Type definitions for runtime-agnostic file system operations.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Options for glob pattern matching.
|
|
8
|
-
*/
|
|
9
|
-
export interface GlobOptions {
|
|
10
|
-
/** Working directory for glob patterns */
|
|
11
|
-
cwd?: string;
|
|
12
|
-
/** Patterns to ignore */
|
|
13
|
-
ignore?: string[];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Runtime-agnostic file system interface.
|
|
18
|
-
* Implementations exist for Bun (optimized) and Node.js (fallback).
|
|
19
|
-
*/
|
|
20
|
-
export interface FileSystem {
|
|
21
|
-
/**
|
|
22
|
-
* Scan the file system for files matching glob patterns.
|
|
23
|
-
* @param patterns - Glob patterns to match
|
|
24
|
-
* @param options - Glob options
|
|
25
|
-
*/
|
|
26
|
-
glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Read a file as a string asynchronously.
|
|
30
|
-
* @param path - Path to the file
|
|
31
|
-
*/
|
|
32
|
-
readFile(path: string): Promise<string>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Read a file as a string synchronously.
|
|
36
|
-
* @param path - Path to the file
|
|
37
|
-
*/
|
|
38
|
-
readFileSync(path: string): string;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Read a file as a Buffer.
|
|
42
|
-
* @param path - Path to the file
|
|
43
|
-
*/
|
|
44
|
-
readFileAsBuffer(path: string): Buffer;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Write contents to a file synchronously.
|
|
48
|
-
* Creates parent directories if they don't exist.
|
|
49
|
-
* @param filepath - Path to write to
|
|
50
|
-
* @param contents - Content to write
|
|
51
|
-
*/
|
|
52
|
-
write(filepath: string, contents: string | Buffer): void;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Write contents to a file asynchronously.
|
|
56
|
-
* @param filepath - Path to write to
|
|
57
|
-
* @param contents - Content to write
|
|
58
|
-
*/
|
|
59
|
-
writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Ensure a directory exists, optionally cleaning it first.
|
|
63
|
-
* @param dirPath - Directory path
|
|
64
|
-
* @param forceCleanup - If true, remove existing directory first
|
|
65
|
-
*/
|
|
66
|
-
ensureDir(dirPath: string, forceCleanup?: boolean): void;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Ensure a directory exists asynchronously, optionally cleaning it first.
|
|
70
|
-
* @param dirPath - Directory path
|
|
71
|
-
* @param forceCleanup - If true, remove existing directory first
|
|
72
|
-
*/
|
|
73
|
-
ensureDirAsync(dirPath: string, forceCleanup?: boolean): Promise<void>;
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Copy a directory recursively.
|
|
77
|
-
* @param source - Source directory
|
|
78
|
-
* @param destination - Destination directory
|
|
79
|
-
*/
|
|
80
|
-
copyDir(source: string, destination: string): void;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Copy a directory recursively asynchronously.
|
|
84
|
-
* @param source - Source directory
|
|
85
|
-
* @param destination - Destination directory
|
|
86
|
-
*/
|
|
87
|
-
copyDirAsync(source: string, destination: string): Promise<void>;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Remove all contents of a directory.
|
|
91
|
-
* @param path - Directory path
|
|
92
|
-
*/
|
|
93
|
-
emptyDir(path: string): void;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Remove all contents of a directory asynchronously.
|
|
97
|
-
* @param path - Directory path
|
|
98
|
-
*/
|
|
99
|
-
emptyDirAsync(path: string): Promise<void>;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Check if a path is a directory.
|
|
103
|
-
* @param path - Path to check
|
|
104
|
-
*/
|
|
105
|
-
isDirectory(path: string): boolean;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Check if a path is a directory asynchronously.
|
|
109
|
-
* @param path - Path to check
|
|
110
|
-
*/
|
|
111
|
-
isDirectoryAsync(path: string): Promise<boolean>;
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Check if a file or directory exists.
|
|
115
|
-
* @param path - Path to check
|
|
116
|
-
*/
|
|
117
|
-
exists(path: string): boolean;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Check if a file or directory exists asynchronously.
|
|
121
|
-
* @param path - Path to check
|
|
122
|
-
*/
|
|
123
|
-
existsAsync(path: string): Promise<boolean>;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Copy a file.
|
|
127
|
-
* @param source - Source file path
|
|
128
|
-
* @param destination - Destination file path
|
|
129
|
-
*/
|
|
130
|
-
copyFile(source: string, destination: string): void;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Copy a file asynchronously.
|
|
134
|
-
* @param source - Source file path
|
|
135
|
-
* @param destination - Destination file path
|
|
136
|
-
*/
|
|
137
|
-
copyFileAsync(source: string, destination: string): Promise<void>;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Remove a file or directory synchronously.
|
|
141
|
-
* @param path - Path to remove
|
|
142
|
-
*/
|
|
143
|
-
remove(path: string): void;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Remove a file or directory asynchronously.
|
|
147
|
-
* @param path - Path to remove
|
|
148
|
-
*/
|
|
149
|
-
removeAsync(path: string): Promise<void>;
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Get a hash of a file's contents.
|
|
153
|
-
* @param path - Path to the file
|
|
154
|
-
*/
|
|
155
|
-
hash(path: string): string;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Gzip a single file.
|
|
159
|
-
* @param path - Path to the file
|
|
160
|
-
*/
|
|
161
|
-
gzipFile(path: string): void;
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Gzip all files with specified extensions in a directory.
|
|
165
|
-
* @param path - Directory path
|
|
166
|
-
* @param extensions - File extensions to gzip (without dot)
|
|
167
|
-
*/
|
|
168
|
-
gzipDir(path: string, extensions: string[]): void;
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Verify that a file exists, throw FileNotFoundError if not.
|
|
172
|
-
* @param path - Path to verify
|
|
173
|
-
* @throws FileNotFoundError
|
|
174
|
-
*/
|
|
175
|
-
verifyFileExists(path: string): void;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Error thrown when a file is not found.
|
|
180
|
-
*/
|
|
181
|
-
export class FileNotFoundError extends Error {
|
|
182
|
-
code = 'ENOENT';
|
|
183
|
-
constructor(path: string) {
|
|
184
|
-
super(`File: ${path} not found`);
|
|
185
|
-
this.name = 'FileNotFoundError';
|
|
186
|
-
}
|
|
187
|
-
}
|
package/src/utils/common.ts
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @ecopages/file-system/utils/common
|
|
3
|
-
* @description Shared utilities used by both Bun and Node adapters.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
7
|
-
import { cp as cpAsync, mkdir as mkdirAsync, readdir, rm as rmAsync, stat as statAsync } from 'node:fs/promises';
|
|
8
|
-
import { dirname, extname, join as pathJoin } from 'node:path';
|
|
9
|
-
import zlib from 'node:zlib';
|
|
10
|
-
import { FileNotFoundError, type GlobOptions } from '../types.ts';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Base implementation for file system operations.
|
|
14
|
-
* Subclasses override specific methods with runtime-optimized versions.
|
|
15
|
-
*/
|
|
16
|
-
export abstract class BaseFileSystem {
|
|
17
|
-
/**
|
|
18
|
-
* Check if a file or directory exists.
|
|
19
|
-
*/
|
|
20
|
-
exists(filePath: string): boolean {
|
|
21
|
-
return existsSync(filePath);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Verify that a file exists, throw FileNotFoundError if not.
|
|
26
|
-
*/
|
|
27
|
-
verifyFileExists(filePath: string): void {
|
|
28
|
-
if (!this.exists(filePath)) {
|
|
29
|
-
throw new FileNotFoundError(filePath);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Read a file as a string synchronously.
|
|
35
|
-
*/
|
|
36
|
-
readFileSync(filePath: string): string {
|
|
37
|
-
try {
|
|
38
|
-
return readFileSync(filePath, 'utf-8');
|
|
39
|
-
} catch (error) {
|
|
40
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
-
throw new Error(`Error reading file: ${filePath}, ${message}`, { cause: error });
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Read a file as a Buffer.
|
|
47
|
-
*/
|
|
48
|
-
readFileAsBuffer(filePath: string): Buffer {
|
|
49
|
-
try {
|
|
50
|
-
return readFileSync(filePath);
|
|
51
|
-
} catch (error) {
|
|
52
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
-
throw new Error(`Error reading file: ${filePath}, ${message}`, { cause: error });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Write contents to a file synchronously.
|
|
59
|
-
*/
|
|
60
|
-
write(filepath: string, contents: string | Buffer): void {
|
|
61
|
-
try {
|
|
62
|
-
this.ensureDir(dirname(filepath));
|
|
63
|
-
writeFileSync(filepath, contents);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
-
throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Ensure a directory exists.
|
|
72
|
-
*/
|
|
73
|
-
ensureDir(dirPath: string, forceCleanup?: boolean): void {
|
|
74
|
-
if (forceCleanup && existsSync(dirPath)) {
|
|
75
|
-
rmSync(dirPath, { recursive: true, force: true });
|
|
76
|
-
}
|
|
77
|
-
mkdirSync(dirPath, { recursive: true });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Ensure a directory exists asynchronously.
|
|
82
|
-
*/
|
|
83
|
-
async ensureDirAsync(dirPath: string, forceCleanup?: boolean): Promise<void> {
|
|
84
|
-
if (forceCleanup && (await this.existsAsync(dirPath))) {
|
|
85
|
-
await rmAsync(dirPath, { recursive: true, force: true });
|
|
86
|
-
}
|
|
87
|
-
await mkdirAsync(dirPath, { recursive: true });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Copy a directory recursively.
|
|
92
|
-
*/
|
|
93
|
-
copyDir(source: string, destination: string): void {
|
|
94
|
-
cpSync(source, destination, { recursive: true });
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Copy a directory recursively asynchronously.
|
|
99
|
-
*/
|
|
100
|
-
async copyDirAsync(source: string, destination: string): Promise<void> {
|
|
101
|
-
await cpAsync(source, destination, { recursive: true });
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Copy a file.
|
|
106
|
-
*/
|
|
107
|
-
copyFile(source: string, destination: string): void {
|
|
108
|
-
cpSync(source, destination);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Remove all contents of a directory.
|
|
113
|
-
*/
|
|
114
|
-
emptyDir(dirPath: string): void {
|
|
115
|
-
if (!this.isDirectory(dirPath)) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const entries = readdirSync(dirPath);
|
|
119
|
-
for (const entry of entries) {
|
|
120
|
-
const fullPath = pathJoin(dirPath, entry);
|
|
121
|
-
rmSync(fullPath, { recursive: true, force: true });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Remove a directory's contents asynchronously.
|
|
127
|
-
*/
|
|
128
|
-
async emptyDirAsync(dirPath: string): Promise<void> {
|
|
129
|
-
if (!(await this.isDirectoryAsync(dirPath))) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const entries = await readdir(dirPath);
|
|
133
|
-
await Promise.all(entries.map((entry) => rmAsync(pathJoin(dirPath, entry), { recursive: true, force: true })));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Remove a file or directory synchronously.
|
|
138
|
-
*/
|
|
139
|
-
remove(filePath: string): void {
|
|
140
|
-
rmSync(filePath, { recursive: true, force: true });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Remove a file or directory asynchronously.
|
|
145
|
-
*/
|
|
146
|
-
async removeAsync(filePath: string): Promise<void> {
|
|
147
|
-
await rmAsync(filePath, { recursive: true, force: true });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Check if a path is a directory.
|
|
152
|
-
*/
|
|
153
|
-
isDirectory(filePath: string): boolean {
|
|
154
|
-
return existsSync(filePath) && statSync(filePath).isDirectory();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Check if a path is a directory asynchronously.
|
|
159
|
-
*/
|
|
160
|
-
async isDirectoryAsync(filePath: string): Promise<boolean> {
|
|
161
|
-
try {
|
|
162
|
-
const stats = await statAsync(filePath);
|
|
163
|
-
return stats.isDirectory();
|
|
164
|
-
} catch {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Gzip a single file.
|
|
171
|
-
*/
|
|
172
|
-
gzipFile(filePath: string): void {
|
|
173
|
-
const data = this.readFileAsBuffer(filePath);
|
|
174
|
-
const compressedData = zlib.gzipSync(Buffer.from(data));
|
|
175
|
-
const gzipFile = `${filePath}.gz`;
|
|
176
|
-
writeFileSync(gzipFile, compressedData);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Gzip all files with specified extensions in a directory.
|
|
181
|
-
*/
|
|
182
|
-
gzipDir(dirPath: string, extensionsToGzip: string[]): void {
|
|
183
|
-
const entries = readdirSync(dirPath, { recursive: true, withFileTypes: true });
|
|
184
|
-
for (const entry of entries) {
|
|
185
|
-
if (entry.isFile()) {
|
|
186
|
-
const ext = extname(entry.name).slice(1);
|
|
187
|
-
if (extensionsToGzip.includes(ext)) {
|
|
188
|
-
this.gzipFile(pathJoin(entry.parentPath, entry.name));
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Glob patterns - must be implemented by runtime-specific adapters.
|
|
196
|
-
*/
|
|
197
|
-
abstract glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Read file async - must be implemented by runtime-specific adapters.
|
|
201
|
-
*/
|
|
202
|
-
abstract readFile(path: string): Promise<string>;
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Write file async - must be implemented by runtime-specific adapters.
|
|
206
|
-
*/
|
|
207
|
-
abstract writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Check if a file exists asynchronously - must be implemented by runtime-specific adapters.
|
|
211
|
-
*/
|
|
212
|
-
abstract existsAsync(filePath: string): Promise<boolean>;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Copy a file asynchronously - must be implemented by runtime-specific adapters.
|
|
216
|
-
*/
|
|
217
|
-
abstract copyFileAsync(source: string, destination: string): Promise<void>;
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Hash file - must be implemented by runtime-specific adapters.
|
|
221
|
-
*/
|
|
222
|
-
abstract hash(path: string): string;
|
|
223
|
-
}
|