@thi.ng/file-io 1.0.4 → 1.0.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-09T19:12:03Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
package/delete.js CHANGED
@@ -1,16 +1,10 @@
1
1
  import { unlinkSync } from "fs";
2
- /**
3
- * Deletes file at given path. If `dryRun` is true (default: false), the file
4
- * WON'T be deleted, however if a `logger` is provided then at least a dry-run
5
- * log message will be emitted.
6
- *
7
- * @param path
8
- * @param logger
9
- * @param dryRun
10
- */
11
- export const deleteFile = (path, logger, dryRun = false) => {
12
- logger && logger.info(`${dryRun ? "[dryrun] " : ""}deleting file: ${path}`);
13
- if (dryRun)
14
- return;
15
- unlinkSync(path);
2
+ const deleteFile = (path, logger, dryRun = false) => {
3
+ logger && logger.info(`${dryRun ? "[dryrun] " : ""}deleting file: ${path}`);
4
+ if (dryRun)
5
+ return;
6
+ unlinkSync(path);
7
+ };
8
+ export {
9
+ deleteFile
16
10
  };
package/dir.js CHANGED
@@ -1,24 +1,11 @@
1
1
  import { existsSync, mkdirSync, statSync } from "fs";
2
2
  import { sep } from "path";
3
- /**
4
- * Checks if the directory for given file `path` already exists, and if not the
5
- * case, creates it. Returns true if the latter case.
6
- *
7
- * @remarks
8
- * If `path` only contains a filename (without any directory structure), the
9
- * function does nothing.
10
- *
11
- * @param path
12
- */
13
- export const ensureDirForFile = (path) => {
14
- const dir = path.substring(0, path.lastIndexOf(sep));
15
- return dir.length > 0 && !existsSync(dir)
16
- ? (mkdirSync(dir, { recursive: true }), true)
17
- : false;
3
+ const ensureDirForFile = (path) => {
4
+ const dir = path.substring(0, path.lastIndexOf(sep));
5
+ return dir.length > 0 && !existsSync(dir) ? (mkdirSync(dir, { recursive: true }), true) : false;
6
+ };
7
+ const isDirectory = (path) => statSync(path).isDirectory();
8
+ export {
9
+ ensureDirForFile,
10
+ isDirectory
18
11
  };
19
- /**
20
- * Returns true if `path` is a directory (assumes path exists).
21
- *
22
- * @param path
23
- */
24
- export const isDirectory = (path) => statSync(path).isDirectory();
package/ext.js CHANGED
@@ -1,9 +1,7 @@
1
- /**
2
- * Returns file extension of given file `path`.
3
- *
4
- * @param path
5
- */
6
- export const fileExt = (path) => {
7
- const match = /\.(\w+)$/.exec(path);
8
- return match ? match[1].toLowerCase() : "";
1
+ const fileExt = (path) => {
2
+ const match = /\.(\w+)$/.exec(path);
3
+ return match ? match[1].toLowerCase() : "";
4
+ };
5
+ export {
6
+ fileExt
9
7
  };
package/file-chunks.js CHANGED
@@ -1,48 +1,38 @@
1
1
  import { U32 } from "@thi.ng/hex";
2
2
  import { open } from "fs/promises";
3
- /**
4
- * Async iterator. Yields chunks of byte buffers from given file. User
5
- * configurable size and byte ranges.
6
- *
7
- * @example
8
- * ```ts
9
- * for await(let buf of fileChunks("file.bin", { start: 16*1024*1024 })) {
10
- * // ...
11
- * }
12
- * ```
13
- *
14
- * @param path
15
- * @param opts
16
- */
17
- export async function* fileChunks(path, opts) {
18
- let { logger, size, start, end } = {
19
- size: 1024,
20
- start: 0,
21
- end: Infinity,
22
- ...opts,
23
- };
24
- logger &&
25
- logger.debug(`start reading file chunks (size: 0x${size}): ${path}`);
26
- let fd = undefined;
27
- try {
28
- fd = await open(path, "r");
29
- while (start < end) {
30
- logger &&
31
- logger.debug(`reading chunk: 0x${U32(start)} - 0x${U32(start + size - 1)} (${path})`);
32
- const { buffer, bytesRead } = await fd.read({
33
- buffer: Buffer.alloc(size),
34
- length: size,
35
- position: start,
36
- });
37
- if (bytesRead === 0)
38
- break;
39
- yield buffer;
40
- if (bytesRead < size)
41
- break;
42
- start += bytesRead;
43
- }
44
- }
45
- finally {
46
- await fd?.close();
3
+ async function* fileChunks(path, opts) {
4
+ let { logger, size, start, end } = {
5
+ size: 1024,
6
+ start: 0,
7
+ end: Infinity,
8
+ ...opts
9
+ };
10
+ logger && logger.debug(`start reading file chunks (size: 0x${size}): ${path}`);
11
+ let fd = void 0;
12
+ try {
13
+ fd = await open(path, "r");
14
+ while (start < end) {
15
+ logger && logger.debug(
16
+ `reading chunk: 0x${U32(start)} - 0x${U32(
17
+ start + size - 1
18
+ )} (${path})`
19
+ );
20
+ const { buffer, bytesRead } = await fd.read({
21
+ buffer: Buffer.alloc(size),
22
+ length: size,
23
+ position: start
24
+ });
25
+ if (bytesRead === 0)
26
+ break;
27
+ yield buffer;
28
+ if (bytesRead < size)
29
+ break;
30
+ start += bytesRead;
47
31
  }
32
+ } finally {
33
+ await fd?.close();
34
+ }
48
35
  }
36
+ export {
37
+ fileChunks
38
+ };
package/files.js CHANGED
@@ -3,81 +3,45 @@ import { isString } from "@thi.ng/checks/is-string";
3
3
  import { readdirSync, statSync } from "fs";
4
4
  import { sep } from "path";
5
5
  import { isDirectory } from "./dir.js";
6
- /**
7
- * Recursively reads given directory (up to given max. depth, default: infinite)
8
- * and yields sequence of file names matching given extension (or regexp or
9
- * predicate).
10
- *
11
- * @remarks
12
- * Files will be matched using their _full_ relative sub-path (starting with
13
- * given `dir`). If NO `match` is given, all files will be matched. Directories
14
- * will *not* be tested and are always traversed (up to given `maxDepth`).
15
- *
16
- * The optional `logger` is only used to log errors for files which couldn't be
17
- * accessed.
18
- *
19
- * @param dir
20
- * @param match
21
- * @param maxDepth
22
- * @param logger
23
- */
24
- export const files = (dir, match = "", maxDepth = Infinity, logger) => __files(dir, match, logger, maxDepth, 0);
6
+ const files = (dir, match = "", maxDepth = Infinity, logger) => __files(dir, match, logger, maxDepth, 0);
25
7
  function* __files(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
26
- if (depth >= maxDepth)
27
- return;
28
- const pred = __ensurePred(match);
29
- for (let f of readdirSync(dir).sort()) {
30
- const curr = dir + sep + f;
31
- try {
32
- if (isDirectory(curr)) {
33
- yield* __files(curr, match, logger, maxDepth, depth + 1);
34
- }
35
- else if (pred(curr)) {
36
- yield curr;
37
- }
38
- }
39
- catch (e) {
40
- logger &&
41
- logger.warn(`ignoring file: ${f} (${e.message})`);
42
- }
8
+ if (depth >= maxDepth)
9
+ return;
10
+ const pred = __ensurePred(match);
11
+ for (let f of readdirSync(dir).sort()) {
12
+ const curr = dir + sep + f;
13
+ try {
14
+ if (isDirectory(curr)) {
15
+ yield* __files(curr, match, logger, maxDepth, depth + 1);
16
+ } else if (pred(curr)) {
17
+ yield curr;
18
+ }
19
+ } catch (e) {
20
+ logger && logger.warn(`ignoring file: ${f} (${e.message})`);
43
21
  }
22
+ }
44
23
  }
45
- /**
46
- * Similar to {@link files}, however yields iterator of only matching
47
- * sub-directories in given `dir`. Normal files are being ignored.
48
- *
49
- * @remarks
50
- * Like the matcher in {@link files}, the regex or predicate will be applied to
51
- * the _full_ sub-path (starting with `dir`) in order to determine a match.
52
- *
53
- * @param dir
54
- * @param match
55
- * @param maxDepth
56
- * @param logger
57
- */
58
- export const dirs = (dir, match = "", maxDepth = Infinity, logger) => __dirs(dir, match, logger, maxDepth, 0);
24
+ const dirs = (dir, match = "", maxDepth = Infinity, logger) => __dirs(dir, match, logger, maxDepth, 0);
59
25
  function* __dirs(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
60
- if (depth >= maxDepth)
61
- return;
62
- const pred = __ensurePred(match);
63
- for (let f of readdirSync(dir).sort()) {
64
- const curr = dir + sep + f;
65
- try {
66
- if (statSync(curr).isDirectory()) {
67
- if (pred(curr))
68
- yield curr;
69
- yield* __dirs(curr, match, logger, maxDepth, depth + 1);
70
- }
71
- }
72
- catch (e) {
73
- logger &&
74
- logger.warn(`ignoring file/dir: ${f} (${e.message})`);
75
- }
26
+ if (depth >= maxDepth)
27
+ return;
28
+ const pred = __ensurePred(match);
29
+ for (let f of readdirSync(dir).sort()) {
30
+ const curr = dir + sep + f;
31
+ try {
32
+ if (statSync(curr).isDirectory()) {
33
+ if (pred(curr))
34
+ yield curr;
35
+ yield* __dirs(curr, match, logger, maxDepth, depth + 1);
36
+ }
37
+ } catch (e) {
38
+ logger && logger.warn(`ignoring file/dir: ${f} (${e.message})`);
76
39
  }
40
+ }
77
41
  }
78
- /** @internal */
79
42
  const __ensureRegEx = (match) => isString(match) ? new RegExp(`${match.replace(/\./g, "\\.")}$`) : match;
80
- const __ensurePred = (match) => isFunction(match)
81
- ? match
82
- : ((match = __ensureRegEx(match)),
83
- (x) => match.test(x));
43
+ const __ensurePred = (match) => isFunction(match) ? match : (match = __ensureRegEx(match), (x) => match.test(x));
44
+ export {
45
+ dirs,
46
+ files
47
+ };
package/hash.js CHANGED
@@ -1,16 +1,20 @@
1
1
  import { createHash } from "crypto";
2
2
  import { readFileSync } from "fs";
3
- export const fileHash = (path, logger, algo = "sha256") => {
4
- const sum = createHash(algo);
5
- sum.update(readFileSync(path));
6
- const hash = sum.digest("hex");
7
- logger && logger.info(`${algo} hash for ${path}: ${hash}`);
8
- return hash;
3
+ const fileHash = (path, logger, algo = "sha256") => {
4
+ const sum = createHash(algo);
5
+ sum.update(readFileSync(path));
6
+ const hash = sum.digest("hex");
7
+ logger && logger.info(`${algo} hash for ${path}: ${hash}`);
8
+ return hash;
9
9
  };
10
- export const stringHash = (src, logger, algo = "sha256") => {
11
- const sum = createHash(algo);
12
- sum.update(src);
13
- const hash = sum.digest("hex");
14
- logger && logger.info(`${algo} hash for string: ${hash}`);
15
- return hash;
10
+ const stringHash = (src, logger, algo = "sha256") => {
11
+ const sum = createHash(algo);
12
+ sum.update(src);
13
+ const hash = sum.digest("hex");
14
+ logger && logger.info(`${algo} hash for string: ${hash}`);
15
+ return hash;
16
+ };
17
+ export {
18
+ fileHash,
19
+ stringHash
16
20
  };
package/json.js CHANGED
@@ -1,18 +1,12 @@
1
1
  import { readText, writeText } from "./text.js";
2
- export const readJSON = (path, logger) => JSON.parse(readText(path, logger));
3
- /**
4
- * Serializes `obj` to JSON and writes result to UTF-8 file `path`. See
5
- * {@link writeText} for more details.
6
- *
7
- * @remarks
8
- * The `replacer` and `space` args are the same as supported by
9
- * `JSON.stringify()`.
10
- *
11
- * @param path
12
- * @param obj
13
- * @param replacer
14
- * @param space
15
- * @param logger
16
- * @param dryRun
17
- */
18
- export const writeJSON = (path, obj, replacer, space, logger, dryRun = false) => writeText(path, JSON.stringify(obj, replacer, space) + "\n", logger, dryRun);
2
+ const readJSON = (path, logger) => JSON.parse(readText(path, logger));
3
+ const writeJSON = (path, obj, replacer, space, logger, dryRun = false) => writeText(
4
+ path,
5
+ JSON.stringify(obj, replacer, space) + "\n",
6
+ logger,
7
+ dryRun
8
+ );
9
+ export {
10
+ readJSON,
11
+ writeJSON
12
+ };
package/mask.js CHANGED
@@ -1,8 +1,4 @@
1
- /**
2
- * Replaces `home` (default: `process.env.HOME`) sub-path with given `mask`
3
- * (default: `~`).
4
- *
5
- * @param path
6
- * @param home
7
- */
8
- export const maskHomeDir = (path, home = process.env.HOME, mask = "~") => (home ? path.replace(home, mask) : path);
1
+ const maskHomeDir = (path, home = process.env.HOME, mask = "~") => home ? path.replace(home, mask) : path;
2
+ export {
3
+ maskHomeDir
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/file-io",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Assorted file I/O utils (with logging support) for NodeJS",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -24,7 +24,9 @@
24
24
  "author": "Karsten Schmidt (https://thi.ng)",
25
25
  "license": "Apache-2.0",
26
26
  "scripts": {
27
- "build": "yarn clean && tsc --declaration",
27
+ "build": "yarn build:esbuild && yarn build:decl",
28
+ "build:decl": "tsc --declaration --emitDeclarationOnly",
29
+ "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
28
30
  "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
29
31
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
30
32
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
@@ -33,14 +35,15 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/api": "^8.9.11",
37
- "@thi.ng/checks": "^3.4.11",
38
- "@thi.ng/hex": "^2.3.23",
39
- "@thi.ng/logger": "^2.0.1",
40
- "@thi.ng/random": "^3.6.17"
38
+ "@thi.ng/api": "^8.9.12",
39
+ "@thi.ng/checks": "^3.4.12",
40
+ "@thi.ng/hex": "^2.3.24",
41
+ "@thi.ng/logger": "^2.0.2",
42
+ "@thi.ng/random": "^3.6.18"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@microsoft/api-extractor": "^7.38.3",
46
+ "esbuild": "^0.19.8",
44
47
  "rimraf": "^5.0.5",
45
48
  "tools": "^0.0.1",
46
49
  "typedoc": "^0.25.4",
@@ -114,5 +117,5 @@
114
117
  "status": "stable",
115
118
  "year": 2022
116
119
  },
117
- "gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
120
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
118
121
  }
package/read.js CHANGED
@@ -1,12 +1,9 @@
1
1
  import { readFileSync } from "fs";
2
- /**
3
- * Reads given file `path` into a byte array.
4
- *
5
- * @param path
6
- * @param logger
7
- */
8
- export const readBinary = (path, logger) => {
9
- logger && logger.debug("reading file:", path);
10
- const buf = readFileSync(path);
11
- return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
2
+ const readBinary = (path, logger) => {
3
+ logger && logger.debug("reading file:", path);
4
+ const buf = readFileSync(path);
5
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
6
+ };
7
+ export {
8
+ readBinary
12
9
  };
package/temp.js CHANGED
@@ -4,11 +4,15 @@ import { realpathSync, writeFileSync } from "fs";
4
4
  import { tmpdir } from "os";
5
5
  import { sep } from "path";
6
6
  import { ensureDirForFile } from "./dir.js";
7
- export const createTempFile = (body, logger, name) => {
8
- const path = tempFilePath(name);
9
- logger && logger.debug("creating temp file:", path);
10
- ensureDirForFile(path);
11
- writeFileSync(path, body, isString(body) ? "utf-8" : undefined);
12
- return path;
7
+ const createTempFile = (body, logger, name) => {
8
+ const path = tempFilePath(name);
9
+ logger && logger.debug("creating temp file:", path);
10
+ ensureDirForFile(path);
11
+ writeFileSync(path, body, isString(body) ? "utf-8" : void 0);
12
+ return path;
13
+ };
14
+ const tempFilePath = (name) => realpathSync(tmpdir()) + sep + (name || randomID(16, "tmp-"));
15
+ export {
16
+ createTempFile,
17
+ tempFilePath
13
18
  };
14
- export const tempFilePath = (name) => realpathSync(tmpdir()) + sep + (name || randomID(16, "tmp-"));
package/text.js CHANGED
@@ -1,26 +1,18 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
2
  import { readFileSync } from "fs";
3
3
  import { writeFile } from "./write.js";
4
- /**
5
- * Reads text from given file `path`, optionally with custom encoding (default:
6
- * UTF-8).
7
- *
8
- * @param path
9
- * @param logger
10
- * @param encoding
11
- */
12
- export const readText = (path, logger, encoding = "utf-8") => {
13
- logger && logger.debug("reading file:", path);
14
- return readFileSync(path, encoding);
4
+ const readText = (path, logger, encoding = "utf-8") => {
5
+ logger && logger.debug("reading file:", path);
6
+ return readFileSync(path, encoding);
7
+ };
8
+ const writeText = (path, body, logger, dryRun = false) => writeFile(
9
+ path,
10
+ isArray(body) ? body.join("\n") : body,
11
+ "utf-8",
12
+ logger,
13
+ dryRun
14
+ );
15
+ export {
16
+ readText,
17
+ writeText
15
18
  };
16
- /**
17
- * Writes `body` as UTF-8 file to given `path`. If `dryRun` is true (default:
18
- * false), the file WON'T be written, however if a `logger` is provided then at
19
- * least a dry-run log message will be emitted.
20
- *
21
- * @param path
22
- * @param body
23
- * @param logger
24
- * @param dryRun
25
- */
26
- export const writeText = (path, body, logger, dryRun = false) => writeFile(path, isArray(body) ? body.join("\n") : body, "utf-8", logger, dryRun);
package/write.js CHANGED
@@ -1,21 +1,13 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
2
  import { writeFileSync } from "fs";
3
3
  import { ensureDirForFile } from "./dir.js";
4
- /**
5
- * Writes `body` as to given `path` (using optional `opts` to define encoding).
6
- * If `dryRun` is true (default: false), the file WON'T be written, however if a
7
- * `logger` is provided then at least a dry-run log message will be emitted.
8
- *
9
- * @param path
10
- * @param body
11
- * @param opts
12
- * @param logger
13
- * @param dryRun
14
- */
15
- export const writeFile = (path, body, opts, logger, dryRun = false) => {
16
- logger && logger.info(`${dryRun ? "[dryrun] " : ""}writing file: ${path}`);
17
- if (dryRun)
18
- return;
19
- ensureDirForFile(path);
20
- writeFileSync(path, body, !opts && isString(body) ? "utf-8" : opts);
4
+ const writeFile = (path, body, opts, logger, dryRun = false) => {
5
+ logger && logger.info(`${dryRun ? "[dryrun] " : ""}writing file: ${path}`);
6
+ if (dryRun)
7
+ return;
8
+ ensureDirForFile(path);
9
+ writeFileSync(path, body, !opts && isString(body) ? "utf-8" : opts);
10
+ };
11
+ export {
12
+ writeFile
21
13
  };