jg-fs 0.0.0-stage → 1.0.1
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/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +73 -2
- package/dist/async/directories/directories.d.ts +78 -0
- package/dist/async/directories/directories.js +118 -0
- package/dist/async/directories/directories.js.map +1 -0
- package/dist/async/directories/index.d.ts +1 -0
- package/dist/async/directories/index.js +3 -0
- package/dist/async/directories/index.js.map +1 -0
- package/dist/async/directories/utilities.d.ts +9 -0
- package/dist/async/directories/utilities.js +22 -0
- package/dist/async/directories/utilities.js.map +1 -0
- package/dist/async/files/files.d.ts +164 -0
- package/dist/async/files/files.js +252 -0
- package/dist/async/files/files.js.map +1 -0
- package/dist/async/files/index.d.ts +1 -0
- package/dist/async/files/index.js +3 -0
- package/dist/async/files/index.js.map +1 -0
- package/dist/async/files/utilities.d.ts +21 -0
- package/dist/async/files/utilities.js +59 -0
- package/dist/async/files/utilities.js.map +1 -0
- package/dist/async/general/general.d.ts +18 -0
- package/dist/async/general/general.js +35 -0
- package/dist/async/general/general.js.map +1 -0
- package/dist/async/general/index.d.ts +1 -0
- package/dist/async/general/index.js +3 -0
- package/dist/async/general/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/shared/constants.d.ts +4 -0
- package/dist/shared/constants.js +11 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/directory-elements.d.ts +16 -0
- package/dist/shared/directory-elements.js +44 -0
- package/dist/shared/directory-elements.js.map +1 -0
- package/dist/shared/errors.d.ts +9 -0
- package/dist/shared/errors.js +11 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.js +11 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/paths.d.ts +23 -0
- package/dist/shared/paths.js +36 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/types.d.ts +31 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/validations.d.ts +76 -0
- package/dist/shared/validations.js +112 -0
- package/dist/shared/validations.js.map +1 -0
- package/dist/sync/directories/directories.d.ts +73 -0
- package/dist/sync/directories/directories.js +112 -0
- package/dist/sync/directories/directories.js.map +1 -0
- package/dist/sync/directories/index.d.ts +1 -0
- package/dist/sync/directories/index.js +3 -0
- package/dist/sync/directories/index.js.map +1 -0
- package/dist/sync/files/files.d.ts +156 -0
- package/dist/sync/files/files.js +243 -0
- package/dist/sync/files/files.js.map +1 -0
- package/dist/sync/files/index.d.ts +1 -0
- package/dist/sync/files/index.js +3 -0
- package/dist/sync/files/index.js.map +1 -0
- package/dist/sync/files/utilities.d.ts +19 -0
- package/dist/sync/files/utilities.js +58 -0
- package/dist/sync/files/utilities.js.map +1 -0
- package/dist/sync/general/general.d.ts +18 -0
- package/dist/sync/general/general.js +35 -0
- package/dist/sync/general/general.js.map +1 -0
- package/dist/sync/general/index.d.ts +1 -0
- package/dist/sync/general/index.js +3 -0
- package/dist/sync/general/index.js.map +1 -0
- package/dist/sync/index.d.ts +6 -0
- package/dist/sync/index.js +10 -0
- package/dist/sync/index.js.map +1 -0
- package/package.json +67 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.1
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Published by CI from `jesusgraterol/jg-packages`, with npm provenance. The code is the same as 1.0.0.
|
|
8
|
+
|
|
9
|
+
## 1.0.0
|
|
10
|
+
|
|
11
|
+
The first release of `jg-fs`, which replaces `fs-utils-async` 1.0.1 and, at `jg-fs/sync`, `fs-utils-sync` 1.2.0.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- The `jg-fs/sync` entry, with the same functions as the async root entry.
|
|
16
|
+
- `ERRORS`, the codes of every error the package throws.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Empty content is allowed when reading and writing text and binary files.
|
|
21
|
+
- `createDirectory` succeeds when the directory already exists.
|
|
22
|
+
- `includeExts` matches extensions case-insensitively.
|
|
23
|
+
- The sync `readDirectory` joins paths with the platform's separator, and the sync `getDirectoryElements` skips entries removed while it reads.
|
|
24
|
+
- The async `getDirectoryElements` reads at most 32 entries at once.
|
|
25
|
+
- `readFile` returns `string` with an encoding and `Buffer` without one.
|
|
26
|
+
- Errors are `Exception`s from `jg-exception`.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Typos in several error messages.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jesus Graterol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# jg-fs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Filesystem helpers for Node.js, async at the root and sync at `jg-fs/sync`: paths, directories, text, JSON, atomic and bounded files.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install jg-fs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
The root entry is async, and `jg-fs/sync` has the same functions, synchronous:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { readJSONFile, writeFileAtomic, writeTextFile } from 'jg-fs';
|
|
17
|
+
import { readTextFile } from 'jg-fs/sync';
|
|
18
|
+
|
|
19
|
+
await writeTextFile('notes/today.md', '# Today'); // creates notes/ when it's missing
|
|
20
|
+
const config = await readJSONFile<{ port: number }>('config.json');
|
|
21
|
+
await writeFileAtomic('state.json', JSON.stringify(state)); // readers never see a partial file
|
|
22
|
+
|
|
23
|
+
const readme = readTextFile('README.md');
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
List, filter, and sort a directory:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { getDirectoryElements } from 'jg-fs';
|
|
30
|
+
|
|
31
|
+
const { files, directories } = await getDirectoryElements('content', {
|
|
32
|
+
includeExts: ['.md'],
|
|
33
|
+
sortByKey: 'creation',
|
|
34
|
+
sortOrder: 'desc',
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Read files from untrusted paths with a size limit, without following a symbolic link:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { isPathWithin, readBoundedFile } from 'jg-fs';
|
|
42
|
+
|
|
43
|
+
if (isPathWithin(uploadsDirectory, path)) {
|
|
44
|
+
const upload = await readBoundedFile(path, 5 * 1024 * 1024);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Failures throw `Exception`s from `jg-exception`, with the codes in `ERRORS`.
|
|
49
|
+
|
|
50
|
+
## Coming from `fs-utils-async` and `fs-utils-sync`
|
|
51
|
+
|
|
52
|
+
| Old | `jg-fs` |
|
|
53
|
+
| -------------------------------- | ---------------------------- |
|
|
54
|
+
| `import … from 'fs-utils-async'` | `import … from 'jg-fs'` |
|
|
55
|
+
| `import … from 'fs-utils-sync'` | `import … from 'jg-fs/sync'` |
|
|
56
|
+
| the codes, not exported | `ERRORS` |
|
|
57
|
+
|
|
58
|
+
Changed behavior:
|
|
59
|
+
|
|
60
|
+
- Empty content is allowed. `readTextFile`, `readBufferFile`, and `readFile` return empty content instead of throwing, and `writeTextFile('')` and `writeBufferFile` with an empty Buffer write empty files. `FILE_CONTENT_IS_EMPTY_OR_INVALID` now means data of the wrong type, or JSON that is empty or can't be parsed or stringified. Code that relied on the throw to detect empty files needs its own check.
|
|
61
|
+
- `createDirectory` succeeds when the directory already exists, keeping its contents. It throws `DIRECTORY_ALREADY_EXISTS` only when a file or something else that isn't a directory is at the path.
|
|
62
|
+
- `includeExts` matches extensions case-insensitively, so `.JSON` and `.json` are the same.
|
|
63
|
+
- The sync `readDirectory` joins paths with the platform's separator, and the sync `getDirectoryElements` skips entries removed while it reads the directory, as the async one already did.
|
|
64
|
+
- The async `getDirectoryElements` reads at most 32 entries at once.
|
|
65
|
+
- `readFile` returns `string` when given an encoding and `Buffer` otherwise, instead of `string | Buffer`.
|
|
66
|
+
- Errors are `Exception`s from `jg-exception`, with the same codes.
|
|
67
|
+
|
|
68
|
+
## Documentation
|
|
69
|
+
|
|
70
|
+
The full API reference is at https://jg-packages.jesusgraterol.dev/packages/jg-fs/.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
[MIT](https://github.com/jesusgraterol/jg-packages/blob/main/packages/jg-fs/LICENSE)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type IDirectoryElementsOptions, type IDirectoryPathElements } from '../../shared/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether a path exists and is a directory.
|
|
4
|
+
* @param path The path.
|
|
5
|
+
* @returns A promise that resolves to whether the path is a directory.
|
|
6
|
+
* @example
|
|
7
|
+
* await isDirectory('dist'); // true
|
|
8
|
+
*/
|
|
9
|
+
export declare const isDirectory: (path: string) => Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Deletes a directory and everything in it. A missing directory is not an error.
|
|
12
|
+
* @param path The directory's path.
|
|
13
|
+
* @returns A promise that resolves once the directory is gone.
|
|
14
|
+
* @example
|
|
15
|
+
* await deleteDirectory('dist');
|
|
16
|
+
*/
|
|
17
|
+
export declare const deleteDirectory: (path: string) => Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a directory, with any missing parents. An existing directory is kept, or replaced with
|
|
20
|
+
* an empty one when `deleteIfExists` is set.
|
|
21
|
+
* @param path The directory's path.
|
|
22
|
+
* @param deleteIfExists Whether to delete an existing directory and its contents first.
|
|
23
|
+
* @returns A promise that resolves once the directory exists.
|
|
24
|
+
* @example
|
|
25
|
+
* await createDirectory('dist/assets'); // creates missing parents too
|
|
26
|
+
* await createDirectory('dist', true); // replaces it with an empty directory
|
|
27
|
+
* @throws
|
|
28
|
+
* - DIRECTORY_ALREADY_EXISTS: if something other than a directory exists at the path
|
|
29
|
+
*/
|
|
30
|
+
export declare const createDirectory: (path: string, deleteIfExists?: boolean) => Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Copies a directory and everything in it, replacing the destination completely.
|
|
33
|
+
* @param srcPath The directory to copy.
|
|
34
|
+
* @param destPath Where to copy it.
|
|
35
|
+
* @returns A promise that resolves once the copy is complete.
|
|
36
|
+
* @example
|
|
37
|
+
* await copyDirectory('templates', 'dist/templates');
|
|
38
|
+
* @throws
|
|
39
|
+
* - NOT_A_DIRECTORY: if the srcPath is not a directory
|
|
40
|
+
*/
|
|
41
|
+
export declare const copyDirectory: (srcPath: string, destPath: string) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a symbolic link to a directory.
|
|
44
|
+
* @param target The directory the link points to.
|
|
45
|
+
* @param path Where to create the link.
|
|
46
|
+
* @returns A promise that resolves once the link exists.
|
|
47
|
+
* @example
|
|
48
|
+
* await createDirectorySymLink('releases/1.4.0', 'current');
|
|
49
|
+
* @throws
|
|
50
|
+
* - NON_EXISTENT_DIRECTORY: if the target directory doesn't exist
|
|
51
|
+
* - NOT_A_DIRECTORY: if the target directory is not considered a directory by the OS
|
|
52
|
+
*/
|
|
53
|
+
export declare const createDirectorySymLink: (target: string, path: string) => Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Lists a directory's contents as paths joined to the directory's path.
|
|
56
|
+
* @param path The directory's path.
|
|
57
|
+
* @param recursive Whether to include the contents of subdirectories.
|
|
58
|
+
* @returns A promise that resolves to the content paths.
|
|
59
|
+
* @example
|
|
60
|
+
* await readDirectory('dist'); // ['dist/index.js', 'dist/assets']
|
|
61
|
+
* await readDirectory('dist', true); // includes the contents of dist/assets
|
|
62
|
+
* @throws
|
|
63
|
+
* - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory
|
|
64
|
+
*/
|
|
65
|
+
export declare const readDirectory: (path: string, recursive?: boolean) => Promise<string[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Lists a directory's elements grouped by kind: directories, files (filtered by extension), and
|
|
68
|
+
* symbolic links, each group sorted. Entries removed while the directory is read are left out, and
|
|
69
|
+
* at most 32 entries are read at once.
|
|
70
|
+
* @param path The directory's path.
|
|
71
|
+
* @param options The extensions to include and how to sort.
|
|
72
|
+
* @returns A promise that resolves to the grouped elements.
|
|
73
|
+
* @example
|
|
74
|
+
* const { files } = await getDirectoryElements('content', { includeExts: ['.md'], sortByKey: 'creation', sortOrder: 'desc' });
|
|
75
|
+
* @throws
|
|
76
|
+
* - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory
|
|
77
|
+
*/
|
|
78
|
+
export declare const getDirectoryElements: (path: string, options?: Partial<IDirectoryElementsOptions>) => Promise<IDirectoryPathElements>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { cp, mkdir, readdir, rm, symlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { Exception } from 'jg-exception';
|
|
4
|
+
import { buildDirectoryElementsOptions, DIRECTORY_ELEMENTS_CONCURRENCY, ERRORS, groupDirectoryElements, notADirectory, } from '../../shared/index.js';
|
|
5
|
+
import { getPathElement } from '../general/index.js';
|
|
6
|
+
import { mapWithConcurrency } from './utilities.js';
|
|
7
|
+
/**
|
|
8
|
+
* Checks whether a path exists and is a directory.
|
|
9
|
+
* @param path The path.
|
|
10
|
+
* @returns A promise that resolves to whether the path is a directory.
|
|
11
|
+
* @example
|
|
12
|
+
* await isDirectory('dist'); // true
|
|
13
|
+
*/
|
|
14
|
+
export const isDirectory = async (path) => (await getPathElement(path))?.isDirectory ?? false;
|
|
15
|
+
/**
|
|
16
|
+
* Deletes a directory and everything in it. A missing directory is not an error.
|
|
17
|
+
* @param path The directory's path.
|
|
18
|
+
* @returns A promise that resolves once the directory is gone.
|
|
19
|
+
* @example
|
|
20
|
+
* await deleteDirectory('dist');
|
|
21
|
+
*/
|
|
22
|
+
export const deleteDirectory = (path) => rm(path, { recursive: true, force: true });
|
|
23
|
+
/**
|
|
24
|
+
* Creates a directory, with any missing parents. An existing directory is kept, or replaced with
|
|
25
|
+
* an empty one when `deleteIfExists` is set.
|
|
26
|
+
* @param path The directory's path.
|
|
27
|
+
* @param deleteIfExists Whether to delete an existing directory and its contents first.
|
|
28
|
+
* @returns A promise that resolves once the directory exists.
|
|
29
|
+
* @example
|
|
30
|
+
* await createDirectory('dist/assets'); // creates missing parents too
|
|
31
|
+
* await createDirectory('dist', true); // replaces it with an empty directory
|
|
32
|
+
* @throws
|
|
33
|
+
* - DIRECTORY_ALREADY_EXISTS: if something other than a directory exists at the path
|
|
34
|
+
*/
|
|
35
|
+
export const createDirectory = async (path, deleteIfExists) => {
|
|
36
|
+
const element = await getPathElement(path);
|
|
37
|
+
if (element !== null && !element.isDirectory) {
|
|
38
|
+
throw new Exception(`The path '${path}' already exists and is not a directory.`, ERRORS.DIRECTORY_ALREADY_EXISTS);
|
|
39
|
+
}
|
|
40
|
+
if (element !== null && deleteIfExists) {
|
|
41
|
+
await deleteDirectory(path);
|
|
42
|
+
}
|
|
43
|
+
await mkdir(path, { recursive: true });
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Copies a directory and everything in it, replacing the destination completely.
|
|
47
|
+
* @param srcPath The directory to copy.
|
|
48
|
+
* @param destPath Where to copy it.
|
|
49
|
+
* @returns A promise that resolves once the copy is complete.
|
|
50
|
+
* @example
|
|
51
|
+
* await copyDirectory('templates', 'dist/templates');
|
|
52
|
+
* @throws
|
|
53
|
+
* - NOT_A_DIRECTORY: if the srcPath is not a directory
|
|
54
|
+
*/
|
|
55
|
+
export const copyDirectory = async (srcPath, destPath) => {
|
|
56
|
+
if (!(await isDirectory(srcPath))) {
|
|
57
|
+
throw notADirectory(srcPath);
|
|
58
|
+
}
|
|
59
|
+
await deleteDirectory(destPath);
|
|
60
|
+
await cp(srcPath, destPath, { recursive: true });
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Creates a symbolic link to a directory.
|
|
64
|
+
* @param target The directory the link points to.
|
|
65
|
+
* @param path Where to create the link.
|
|
66
|
+
* @returns A promise that resolves once the link exists.
|
|
67
|
+
* @example
|
|
68
|
+
* await createDirectorySymLink('releases/1.4.0', 'current');
|
|
69
|
+
* @throws
|
|
70
|
+
* - NON_EXISTENT_DIRECTORY: if the target directory doesn't exist
|
|
71
|
+
* - NOT_A_DIRECTORY: if the target directory is not considered a directory by the OS
|
|
72
|
+
*/
|
|
73
|
+
export const createDirectorySymLink = async (target, path) => {
|
|
74
|
+
const element = await getPathElement(target);
|
|
75
|
+
if (element === null) {
|
|
76
|
+
throw new Exception(`The target directory '${target}' does not exist.`, ERRORS.NON_EXISTENT_DIRECTORY);
|
|
77
|
+
}
|
|
78
|
+
if (!element.isDirectory) {
|
|
79
|
+
throw notADirectory(target);
|
|
80
|
+
}
|
|
81
|
+
await symlink(target, path, 'dir');
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Lists a directory's contents as paths joined to the directory's path.
|
|
85
|
+
* @param path The directory's path.
|
|
86
|
+
* @param recursive Whether to include the contents of subdirectories.
|
|
87
|
+
* @returns A promise that resolves to the content paths.
|
|
88
|
+
* @example
|
|
89
|
+
* await readDirectory('dist'); // ['dist/index.js', 'dist/assets']
|
|
90
|
+
* await readDirectory('dist', true); // includes the contents of dist/assets
|
|
91
|
+
* @throws
|
|
92
|
+
* - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory
|
|
93
|
+
*/
|
|
94
|
+
export const readDirectory = async (path, recursive = false) => {
|
|
95
|
+
if (!(await isDirectory(path))) {
|
|
96
|
+
throw notADirectory(path);
|
|
97
|
+
}
|
|
98
|
+
const contentPaths = await readdir(path, { encoding: 'utf-8', recursive });
|
|
99
|
+
return contentPaths.map((contentPath) => join(path, contentPath));
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Lists a directory's elements grouped by kind: directories, files (filtered by extension), and
|
|
103
|
+
* symbolic links, each group sorted. Entries removed while the directory is read are left out, and
|
|
104
|
+
* at most 32 entries are read at once.
|
|
105
|
+
* @param path The directory's path.
|
|
106
|
+
* @param options The extensions to include and how to sort.
|
|
107
|
+
* @returns A promise that resolves to the grouped elements.
|
|
108
|
+
* @example
|
|
109
|
+
* const { files } = await getDirectoryElements('content', { includeExts: ['.md'], sortByKey: 'creation', sortOrder: 'desc' });
|
|
110
|
+
* @throws
|
|
111
|
+
* - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory
|
|
112
|
+
*/
|
|
113
|
+
export const getDirectoryElements = async (path, options) => {
|
|
114
|
+
const contentPaths = await readDirectory(path);
|
|
115
|
+
const elements = (await mapWithConcurrency(contentPaths, DIRECTORY_ELEMENTS_CONCURRENCY, getPathElement)).filter((element) => element !== null);
|
|
116
|
+
return groupDirectoryElements(elements, buildDirectoryElementsOptions(options));
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=directories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directories.js","sourceRoot":"","sources":["../../../src/async/directories/directories.ts"],"names":[],"mappings":"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","sourcesContent":["import { cp, mkdir, readdir, rm, symlink } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { Exception } from 'jg-exception';\n\nimport {\n buildDirectoryElementsOptions,\n DIRECTORY_ELEMENTS_CONCURRENCY,\n ERRORS,\n groupDirectoryElements,\n notADirectory,\n type IDirectoryElementsOptions,\n type IDirectoryPathElements,\n type IPathElement,\n} from '../../shared/index.js';\nimport { getPathElement } from '../general/index.js';\n\nimport { mapWithConcurrency } from './utilities.js';\n\n/**\n * Checks whether a path exists and is a directory.\n * @param path The path.\n * @returns A promise that resolves to whether the path is a directory.\n * @example\n * await isDirectory('dist'); // true\n */\nexport const isDirectory = async (path: string): Promise<boolean> =>\n (await getPathElement(path))?.isDirectory ?? false;\n\n/**\n * Deletes a directory and everything in it. A missing directory is not an error.\n * @param path The directory's path.\n * @returns A promise that resolves once the directory is gone.\n * @example\n * await deleteDirectory('dist');\n */\nexport const deleteDirectory = (path: string): Promise<void> =>\n rm(path, { recursive: true, force: true });\n\n/**\n * Creates a directory, with any missing parents. An existing directory is kept, or replaced with\n * an empty one when `deleteIfExists` is set.\n * @param path The directory's path.\n * @param deleteIfExists Whether to delete an existing directory and its contents first.\n * @returns A promise that resolves once the directory exists.\n * @example\n * await createDirectory('dist/assets'); // creates missing parents too\n * await createDirectory('dist', true); // replaces it with an empty directory\n * @throws\n * - DIRECTORY_ALREADY_EXISTS: if something other than a directory exists at the path\n */\nexport const createDirectory = async (path: string, deleteIfExists?: boolean): Promise<void> => {\n const element = await getPathElement(path);\n if (element !== null && !element.isDirectory) {\n throw new Exception(\n `The path '${path}' already exists and is not a directory.`,\n ERRORS.DIRECTORY_ALREADY_EXISTS,\n );\n }\n if (element !== null && deleteIfExists) {\n await deleteDirectory(path);\n }\n await mkdir(path, { recursive: true });\n};\n\n/**\n * Copies a directory and everything in it, replacing the destination completely.\n * @param srcPath The directory to copy.\n * @param destPath Where to copy it.\n * @returns A promise that resolves once the copy is complete.\n * @example\n * await copyDirectory('templates', 'dist/templates');\n * @throws\n * - NOT_A_DIRECTORY: if the srcPath is not a directory\n */\nexport const copyDirectory = async (srcPath: string, destPath: string): Promise<void> => {\n if (!(await isDirectory(srcPath))) {\n throw notADirectory(srcPath);\n }\n await deleteDirectory(destPath);\n await cp(srcPath, destPath, { recursive: true });\n};\n\n/**\n * Creates a symbolic link to a directory.\n * @param target The directory the link points to.\n * @param path Where to create the link.\n * @returns A promise that resolves once the link exists.\n * @example\n * await createDirectorySymLink('releases/1.4.0', 'current');\n * @throws\n * - NON_EXISTENT_DIRECTORY: if the target directory doesn't exist\n * - NOT_A_DIRECTORY: if the target directory is not considered a directory by the OS\n */\nexport const createDirectorySymLink = async (target: string, path: string): Promise<void> => {\n const element = await getPathElement(target);\n if (element === null) {\n throw new Exception(\n `The target directory '${target}' does not exist.`,\n ERRORS.NON_EXISTENT_DIRECTORY,\n );\n }\n if (!element.isDirectory) {\n throw notADirectory(target);\n }\n await symlink(target, path, 'dir');\n};\n\n/**\n * Lists a directory's contents as paths joined to the directory's path.\n * @param path The directory's path.\n * @param recursive Whether to include the contents of subdirectories.\n * @returns A promise that resolves to the content paths.\n * @example\n * await readDirectory('dist'); // ['dist/index.js', 'dist/assets']\n * await readDirectory('dist', true); // includes the contents of dist/assets\n * @throws\n * - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory\n */\nexport const readDirectory = async (\n path: string,\n recursive: boolean = false,\n): Promise<string[]> => {\n if (!(await isDirectory(path))) {\n throw notADirectory(path);\n }\n const contentPaths = await readdir(path, { encoding: 'utf-8', recursive });\n return contentPaths.map((contentPath) => join(path, contentPath));\n};\n\n/**\n * Lists a directory's elements grouped by kind: directories, files (filtered by extension), and\n * symbolic links, each group sorted. Entries removed while the directory is read are left out, and\n * at most 32 entries are read at once.\n * @param path The directory's path.\n * @param options The extensions to include and how to sort.\n * @returns A promise that resolves to the grouped elements.\n * @example\n * const { files } = await getDirectoryElements('content', { includeExts: ['.md'], sortByKey: 'creation', sortOrder: 'desc' });\n * @throws\n * - NOT_A_DIRECTORY: if the path doesn't exist or is not a directory\n */\nexport const getDirectoryElements = async (\n path: string,\n options?: Partial<IDirectoryElementsOptions>,\n): Promise<IDirectoryPathElements> => {\n const contentPaths = await readDirectory(path);\n const elements = (\n await mapWithConcurrency(contentPaths, DIRECTORY_ELEMENTS_CONCURRENCY, getPathElement)\n ).filter((element): element is IPathElement => element !== null);\n return groupDirectoryElements(elements, buildDirectoryElementsOptions(options));\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { copyDirectory, createDirectory, createDirectorySymLink, deleteDirectory, getDirectoryElements, isDirectory, readDirectory, } from './directories.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/async/directories/index.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EACL,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,WAAW,EACX,aAAa,GACd,MAAM,kBAAkB,CAAC","sourcesContent":["// directory actions\nexport {\n copyDirectory,\n createDirectory,\n createDirectorySymLink,\n deleteDirectory,\n getDirectoryElements,\n isDirectory,\n readDirectory,\n} from './directories.js';\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps items with an async function, running at most a fixed number of calls at once, and keeps
|
|
3
|
+
* the results in the items' order.
|
|
4
|
+
* @param items The items.
|
|
5
|
+
* @param concurrency The most calls running at once.
|
|
6
|
+
* @param mapper The async function.
|
|
7
|
+
* @returns A promise that resolves to the results.
|
|
8
|
+
*/
|
|
9
|
+
export declare const mapWithConcurrency: <TItem, TResult>(items: TItem[], concurrency: number, mapper: (item: TItem) => Promise<TResult>) => Promise<TResult[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps items with an async function, running at most a fixed number of calls at once, and keeps
|
|
3
|
+
* the results in the items' order.
|
|
4
|
+
* @param items The items.
|
|
5
|
+
* @param concurrency The most calls running at once.
|
|
6
|
+
* @param mapper The async function.
|
|
7
|
+
* @returns A promise that resolves to the results.
|
|
8
|
+
*/
|
|
9
|
+
export const mapWithConcurrency = async (items, concurrency, mapper) => {
|
|
10
|
+
const results = new Array(items.length);
|
|
11
|
+
let nextIndex = 0;
|
|
12
|
+
const worker = async () => {
|
|
13
|
+
while (nextIndex < items.length) {
|
|
14
|
+
const index = nextIndex;
|
|
15
|
+
nextIndex += 1;
|
|
16
|
+
results[index] = await mapper(items[index]);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));
|
|
20
|
+
return results;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=utilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utilities.js","sourceRoot":"","sources":["../../../src/async/directories/utilities.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,KAAc,EACd,WAAmB,EACnB,MAAyC,EACrB,EAAE;IACtB,MAAM,OAAO,GAAc,IAAI,KAAK,CAAU,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5D,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACvC,OAAO,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,SAAS,CAAC;YACxB,SAAS,IAAI,CAAC,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAU,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;IACvF,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["/**\n * Maps items with an async function, running at most a fixed number of calls at once, and keeps\n * the results in the items' order.\n * @param items The items.\n * @param concurrency The most calls running at once.\n * @param mapper The async function.\n * @returns A promise that resolves to the results.\n */\nexport const mapWithConcurrency = async <TItem, TResult>(\n items: TItem[],\n concurrency: number,\n mapper: (item: TItem) => Promise<TResult>,\n): Promise<TResult[]> => {\n const results: TResult[] = new Array<TResult>(items.length);\n let nextIndex = 0;\n const worker = async (): Promise<void> => {\n while (nextIndex < items.length) {\n const index = nextIndex;\n nextIndex += 1;\n results[index] = await mapper(items[index] as TItem);\n }\n };\n await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, worker));\n return results;\n};\n"]}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import type { WriteFileOptions } from 'node:fs';
|
|
3
|
+
import { type IReadBufferFileOptions, type IReadFileOptions, type IReadStringFileOptions } from '../../shared/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Checks whether a path exists and is a regular file.
|
|
6
|
+
* @param path The path.
|
|
7
|
+
* @returns A promise that resolves to whether the path is a file.
|
|
8
|
+
* @example
|
|
9
|
+
* await isFile('package.json'); // true
|
|
10
|
+
*/
|
|
11
|
+
export declare const isFile: (path: string) => Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* Writes a file, creating its directory first when it doesn't exist.
|
|
14
|
+
* @param path The file's path.
|
|
15
|
+
* @param data The content.
|
|
16
|
+
* @param options Node.js's writeFile options, such as the encoding or mode.
|
|
17
|
+
* @returns A promise that resolves once the file is written.
|
|
18
|
+
* @example
|
|
19
|
+
* await writeFile('out/report.csv', csv, { encoding: 'utf-8' });
|
|
20
|
+
*/
|
|
21
|
+
export declare const writeFile: (path: string, data: string | NodeJS.ArrayBufferView, options?: WriteFileOptions) => Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Writes a UTF-8 text file. An empty string writes an empty file.
|
|
24
|
+
* @param path The file's path.
|
|
25
|
+
* @param data The text.
|
|
26
|
+
* @returns A promise that resolves once the file is written.
|
|
27
|
+
* @example
|
|
28
|
+
* await writeTextFile('notes/today.md', '# Today');
|
|
29
|
+
* @throws
|
|
30
|
+
* - FILE_CONTENT_IS_EMPTY_OR_INVALID: if the data isn't a string
|
|
31
|
+
*/
|
|
32
|
+
export declare const writeTextFile: (path: string, data: string) => Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Writes a JSON file. A string is written as given; anything else is stringified.
|
|
35
|
+
* @param path The file's path.
|
|
36
|
+
* @param data The value, or its JSON text.
|
|
37
|
+
* @param space The indentation for stringified values.
|
|
38
|
+
* @returns A promise that resolves once the file is written.
|
|
39
|
+
* @example
|
|
40
|
+
* await writeJSONFile('config.json', { port: 8080 });
|
|
41
|
+
* @throws
|
|
42
|
+
* - FILE_CONTENT_IS_EMPTY_OR_INVALID: if the JSON content cannot be stringified or is empty
|
|
43
|
+
*/
|
|
44
|
+
export declare const writeJSONFile: <T = Record<string, unknown>>(path: string, data: T | string, space?: number) => Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Writes a binary file. An empty Buffer writes an empty file.
|
|
47
|
+
* @param path The file's path.
|
|
48
|
+
* @param data The content.
|
|
49
|
+
* @returns A promise that resolves once the file is written.
|
|
50
|
+
* @example
|
|
51
|
+
* await writeBufferFile('avatar.png', imageBuffer);
|
|
52
|
+
* @throws
|
|
53
|
+
* - FILE_CONTENT_IS_EMPTY_OR_INVALID: if the data isn't a Buffer
|
|
54
|
+
*/
|
|
55
|
+
export declare const writeBufferFile: (path: string, data: Buffer) => Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Reads a file as a string when an encoding is given, or as a Buffer otherwise.
|
|
58
|
+
* @param path The file's path.
|
|
59
|
+
* @param options Node.js's readFile options, such as the encoding.
|
|
60
|
+
* @returns A promise that resolves to the content.
|
|
61
|
+
* @example
|
|
62
|
+
* await readFile('notes.md', 'utf-8'); // a string
|
|
63
|
+
* await readFile('avatar.png'); // a Buffer
|
|
64
|
+
* @throws
|
|
65
|
+
* - NOT_A_FILE: if the path doesn't exist or is not a file
|
|
66
|
+
*/
|
|
67
|
+
export declare function readFile(path: string, options: IReadStringFileOptions): Promise<string>;
|
|
68
|
+
export declare function readFile(path: string, options?: IReadBufferFileOptions): Promise<Buffer>;
|
|
69
|
+
export declare function readFile(path: string, options?: IReadFileOptions): Promise<string | Buffer>;
|
|
70
|
+
/**
|
|
71
|
+
* Reads a UTF-8 text file. An empty file reads as an empty string.
|
|
72
|
+
* @param path The file's path.
|
|
73
|
+
* @returns A promise that resolves to the text.
|
|
74
|
+
* @example
|
|
75
|
+
* const markdown = await readTextFile('README.md');
|
|
76
|
+
* @throws
|
|
77
|
+
* - NOT_A_FILE: if the path doesn't exist or is not a file
|
|
78
|
+
*/
|
|
79
|
+
export declare const readTextFile: (path: string) => Promise<string>;
|
|
80
|
+
/**
|
|
81
|
+
* Reads and parses a JSON file.
|
|
82
|
+
* @param path The file's path.
|
|
83
|
+
* @returns A promise that resolves to the parsed value.
|
|
84
|
+
* @example
|
|
85
|
+
* const config = await readJSONFile<{ port: number }>('config.json');
|
|
86
|
+
* @throws
|
|
87
|
+
* - NOT_A_FILE: if the path doesn't exist or is not a file
|
|
88
|
+
* - FILE_CONTENT_IS_EMPTY_OR_INVALID: if the file's JSON content cannot be parsed
|
|
89
|
+
*/
|
|
90
|
+
export declare const readJSONFile: <T = Record<string, unknown>>(path: string) => Promise<T>;
|
|
91
|
+
/**
|
|
92
|
+
* Reads a binary file. An empty file reads as an empty Buffer.
|
|
93
|
+
* @param path The file's path.
|
|
94
|
+
* @returns A promise that resolves to the content.
|
|
95
|
+
* @example
|
|
96
|
+
* const image = await readBufferFile('avatar.png');
|
|
97
|
+
* @throws
|
|
98
|
+
* - NOT_A_FILE: if the path doesn't exist or is not a file
|
|
99
|
+
*/
|
|
100
|
+
export declare const readBufferFile: (path: string) => Promise<Buffer>;
|
|
101
|
+
/**
|
|
102
|
+
* Copies a file, replacing the destination when it exists.
|
|
103
|
+
* @param srcPath The file to copy.
|
|
104
|
+
* @param destPath Where to copy it.
|
|
105
|
+
* @returns A promise that resolves once the copy exists.
|
|
106
|
+
* @example
|
|
107
|
+
* await copyFile('config.example.json', 'config.json');
|
|
108
|
+
* @throws
|
|
109
|
+
* - NOT_A_FILE: if the srcPath doesn't exist or is not a file
|
|
110
|
+
*/
|
|
111
|
+
export declare const copyFile: (srcPath: string, destPath: string) => Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Deletes a file.
|
|
114
|
+
* @param path The file's path.
|
|
115
|
+
* @returns A promise that resolves once the file is gone.
|
|
116
|
+
* @example
|
|
117
|
+
* await deleteFile('cache.json');
|
|
118
|
+
* @throws
|
|
119
|
+
* - NOT_A_FILE: if the path doesn't exist or is not a file
|
|
120
|
+
*/
|
|
121
|
+
export declare const deleteFile: (path: string) => Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Creates a symbolic link to a file.
|
|
124
|
+
* @param target The file the link points to.
|
|
125
|
+
* @param path Where to create the link.
|
|
126
|
+
* @returns A promise that resolves once the link exists.
|
|
127
|
+
* @example
|
|
128
|
+
* await createFileSymLink('config.production.json', 'config.json');
|
|
129
|
+
* @throws
|
|
130
|
+
* - NON_EXISTENT_FILE: if the target file does not exist
|
|
131
|
+
* - NOT_A_FILE: if the target is not a file
|
|
132
|
+
*/
|
|
133
|
+
export declare const createFileSymLink: (target: string, path: string) => Promise<void>;
|
|
134
|
+
/**
|
|
135
|
+
* Writes a file atomically: readers see either the previous content or the complete new content,
|
|
136
|
+
* never a partial write. The data goes to a temporary file in the same directory, which is flushed
|
|
137
|
+
* to disk and renamed over the path; on POSIX, the directory is flushed too. An explicit mode wins;
|
|
138
|
+
* without one, a replaced file keeps its permission bits and a new file gets the platform default.
|
|
139
|
+
* The temporary file is never more widely readable than the final mode. When writing fails before
|
|
140
|
+
* the rename, the temporary file is removed and the destination is untouched.
|
|
141
|
+
* @param path The file's path.
|
|
142
|
+
* @param data The content.
|
|
143
|
+
* @param mode The permission bits of the written file, such as `0o600`.
|
|
144
|
+
* @returns A promise that resolves once the file is written and flushed.
|
|
145
|
+
* @example
|
|
146
|
+
* await writeFileAtomic('state.json', JSON.stringify(state)); // readers never see a partial file
|
|
147
|
+
* await writeFileAtomic('secrets.env', contents, 0o600);
|
|
148
|
+
*/
|
|
149
|
+
export declare const writeFileAtomic: (path: string, data: string | NodeJS.ArrayBufferView, mode?: number) => Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* Reads a regular file of at most `maxBytes` bytes. It never follows a symbolic link in the
|
|
152
|
+
* path's last component, though links in parent directories are followed, so callers that need
|
|
153
|
+
* containment combine it with `isPathWithin`. It reads at most `maxBytes + 1` bytes, even when the
|
|
154
|
+
* file grows while it's read.
|
|
155
|
+
* @param path The file's path.
|
|
156
|
+
* @param maxBytes The largest size accepted, in bytes.
|
|
157
|
+
* @returns A promise that resolves to the content.
|
|
158
|
+
* @example
|
|
159
|
+
* const upload = await readBoundedFile(join(uploadsDirectory, name), 5 * 1024 * 1024); // at most 5 MB
|
|
160
|
+
* @throws
|
|
161
|
+
* - NOT_A_FILE: if the path doesn't exist, its final component is a symbolic link, or it isn't a regular file
|
|
162
|
+
* - FILE_TOO_LARGE: if the file holds more than maxBytes bytes
|
|
163
|
+
*/
|
|
164
|
+
export declare const readBoundedFile: (path: string, maxBytes: number) => Promise<Buffer>;
|