@thi.ng/file-io 1.1.5 → 1.2.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-01-23T15:58:27Z
3
+ - **Last updated**: 2024-01-29T20:39:36Z
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.
@@ -9,6 +9,16 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [1.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/file-io@1.2.0) (2024-01-26)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add fileWatcher() ([71c1d8f](https://github.com/thi-ng/umbrella/commit/71c1d8f))
17
+
18
+ #### ♻️ Refactoring
19
+
20
+ - migrate internal predicates ([52c985a](https://github.com/thi-ng/umbrella/commit/52c985a))
21
+
12
22
  ## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/file-io@1.1.0) (2023-12-18)
13
23
 
14
24
  #### 🚀 Features
package/README.md CHANGED
@@ -7,9 +7,13 @@
7
7
  ![npm downloads](https://img.shields.io/npm/dm/@thi.ng/file-io.svg)
8
8
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
9
9
 
10
- This is a standalone project, maintained as part of the
11
- [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo and
12
- anti-framework.
10
+ > [!NOTE]
11
+ > This is one of 189 standalone projects, maintained as part
12
+ > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
13
+ > and anti-framework.
14
+ >
15
+ > 🚀 Help me to work full-time on these projects by [sponsoring me on
16
+ > GitHub](https://github.com/sponsors/postspectacular). Thank you! ❤️
13
17
 
14
18
  - [About](#about)
15
19
  - [Status](#status)
@@ -45,7 +49,7 @@ For Node.js REPL:
45
49
  const fileIo = await import("@thi.ng/file-io");
46
50
  ```
47
51
 
48
- Package sizes (brotli'd, pre-treeshake): ESM: 1.30 KB
52
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.98 KB
49
53
 
50
54
  ## Dependencies
51
55
 
package/files.js CHANGED
@@ -1,8 +1,7 @@
1
- import { isFunction } from "@thi.ng/checks/is-function";
2
- import { isString } from "@thi.ng/checks/is-string";
3
1
  import { readdirSync, statSync } from "fs";
4
2
  import { sep } from "path";
5
3
  import { isDirectory } from "./dir.js";
4
+ import { __ensurePred } from "./internal/ensure.js";
6
5
  const files = (dir, match = "", maxDepth = Infinity, logger) => __files(dir, match, logger, maxDepth, 0);
7
6
  function* __files(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
8
7
  if (depth >= maxDepth)
@@ -39,8 +38,6 @@ function* __dirs(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
39
38
  }
40
39
  }
41
40
  }
42
- const __ensureRegEx = (match) => isString(match) ? new RegExp(`${match.replace(/\./g, "\\.")}$`) : match;
43
- const __ensurePred = (match) => isFunction(match) ? match : (match = __ensureRegEx(match), (x) => match.test(x));
44
41
  export {
45
42
  dirs,
46
43
  files
package/index.d.ts CHANGED
@@ -9,5 +9,6 @@ export * from "./mask.js";
9
9
  export * from "./read.js";
10
10
  export * from "./temp.js";
11
11
  export * from "./text.js";
12
+ export * from "./watch.js";
12
13
  export * from "./write.js";
13
14
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -9,4 +9,5 @@ export * from "./mask.js";
9
9
  export * from "./read.js";
10
10
  export * from "./temp.js";
11
11
  export * from "./text.js";
12
+ export * from "./watch.js";
12
13
  export * from "./write.js";
@@ -0,0 +1,6 @@
1
+ import type { Predicate } from "@thi.ng/api";
2
+ /** @internal */
3
+ export declare const __ensureRegEx: (match: string | RegExp | Predicate<string>) => RegExp | Predicate<string>;
4
+ /** @internal */
5
+ export declare const __ensurePred: (match: string | RegExp | Predicate<string>) => Predicate<string>;
6
+ //# sourceMappingURL=ensure.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { isFunction } from "@thi.ng/checks/is-function";
2
+ import { isString } from "@thi.ng/checks/is-string";
3
+ const __ensureRegEx = (match) => isString(match) ? new RegExp(`${match.replace(/\./g, "\\.")}$`) : match;
4
+ const __ensurePred = (match) => isFunction(match) ? match : (match = __ensureRegEx(match), (x) => match.test(x));
5
+ export {
6
+ __ensurePred,
7
+ __ensureRegEx
8
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/file-io",
3
- "version": "1.1.5",
3
+ "version": "1.2.1",
4
4
  "description": "Assorted file I/O utils (with logging support) for NodeJS",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -35,11 +35,11 @@
35
35
  "test": "bun test"
36
36
  },
37
37
  "dependencies": {
38
- "@thi.ng/api": "^8.9.17",
39
- "@thi.ng/checks": "^3.4.17",
40
- "@thi.ng/hex": "^2.3.29",
41
- "@thi.ng/logger": "^2.1.4",
42
- "@thi.ng/random": "^3.6.24"
38
+ "@thi.ng/api": "^8.9.19",
39
+ "@thi.ng/checks": "^3.4.19",
40
+ "@thi.ng/hex": "^2.3.31",
41
+ "@thi.ng/logger": "^2.1.5",
42
+ "@thi.ng/random": "^3.6.26"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@microsoft/api-extractor": "^7.39.0",
@@ -69,7 +69,8 @@
69
69
  },
70
70
  "files": [
71
71
  "./*.js",
72
- "./*.d.ts"
72
+ "./*.d.ts",
73
+ "internal"
73
74
  ],
74
75
  "exports": {
75
76
  ".": {
@@ -108,6 +109,9 @@
108
109
  "./text": {
109
110
  "default": "./text.js"
110
111
  },
112
+ "./watch": {
113
+ "default": "./watch.js"
114
+ },
111
115
  "./write": {
112
116
  "default": "./write.js"
113
117
  }
@@ -116,5 +120,5 @@
116
120
  "status": "stable",
117
121
  "year": 2022
118
122
  },
119
- "gitHead": "417b5a7ea7bd54a3b4f086fe0fc2ce8e8933c9b2\n"
123
+ "gitHead": "20909ffc46f23f61ec7647e8d27ed17752ce9828\n"
120
124
  }
package/watch.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ /// <reference types="node" />
2
+ import { type Event, type IClear, type INotify, type IObjectOf, type Listener, type Predicate } from "@thi.ng/api";
3
+ import type { ILogger } from "@thi.ng/logger";
4
+ import { type FSWatcher } from "fs";
5
+ export interface WatcherOpts {
6
+ /**
7
+ * Number of milliseconds between observing a file change and notifying
8
+ * attached listeners.
9
+ *
10
+ * @defaultValue 100
11
+ */
12
+ delay: number;
13
+ /**
14
+ * Logger instance for showing debug info (must have DEBUG level enabled)
15
+ */
16
+ logger: ILogger;
17
+ }
18
+ export interface PathWatchOpts {
19
+ recursive: boolean;
20
+ ext: string | RegExp | Predicate<string>;
21
+ }
22
+ export declare const EVENT_ADDED = "added";
23
+ export declare const EVENT_CHANGED = "changed";
24
+ export declare const EVENT_REMOVED = "removed";
25
+ export declare class Watcher implements IClear, INotify {
26
+ watchers: IObjectOf<FSWatcher>;
27
+ opts: WatcherOpts;
28
+ protected _signal: boolean;
29
+ constructor(opts?: Partial<WatcherOpts>);
30
+ addAll(paths: (string | [string, Partial<PathWatchOpts>])[]): void;
31
+ add(path: string, opts?: Partial<PathWatchOpts>): boolean;
32
+ remove(path: string): boolean;
33
+ removeAll(paths: Iterable<string>): void;
34
+ clear(): void;
35
+ installSignalHandler(): void;
36
+ addListener(id: string, fn: Listener<string>, scope?: any): boolean;
37
+ removeListener(id: string, fn: Listener<string>, scope?: any): boolean;
38
+ notify(event: Event<string>): boolean;
39
+ }
40
+ export declare const fileWatcher: (opts?: Partial<WatcherOpts>) => Watcher;
41
+ //# sourceMappingURL=watch.d.ts.map
package/watch.js ADDED
@@ -0,0 +1,119 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ INotifyMixin
14
+ } from "@thi.ng/api";
15
+ import { isString } from "@thi.ng/checks/is-string";
16
+ import { NULL_LOGGER } from "@thi.ng/logger/null";
17
+ import { watch as $watch, existsSync } from "fs";
18
+ import { join } from "path";
19
+ import { isDirectory } from "./dir.js";
20
+ import { __ensurePred } from "./internal/ensure.js";
21
+ const EVENT_ADDED = "added";
22
+ const EVENT_CHANGED = "changed";
23
+ const EVENT_REMOVED = "removed";
24
+ let Watcher = class {
25
+ watchers = {};
26
+ opts;
27
+ _signal = false;
28
+ constructor(opts) {
29
+ this.opts = {
30
+ logger: NULL_LOGGER,
31
+ delay: 100,
32
+ ...opts
33
+ };
34
+ }
35
+ addAll(paths) {
36
+ for (let p of paths) {
37
+ isString(p) ? this.add(p) : this.add(...p);
38
+ }
39
+ }
40
+ add(path, opts) {
41
+ if (this.watchers[path])
42
+ return false;
43
+ const isDir = isDirectory(path);
44
+ const pred = __ensurePred(opts?.ext || "");
45
+ this.opts.logger.debug(`adding watcher for: ${path}`);
46
+ this.watchers[path] = $watch(
47
+ path,
48
+ { recursive: opts?.recursive !== false },
49
+ (event, currPath) => {
50
+ if (!currPath)
51
+ return;
52
+ currPath = isDir ? join(path, currPath) : path;
53
+ if (!pred(currPath))
54
+ return;
55
+ setTimeout(() => {
56
+ if (event === "change") {
57
+ this.opts.logger.info(`file changed: ${currPath}`);
58
+ this.notify({
59
+ id: EVENT_CHANGED,
60
+ value: currPath
61
+ });
62
+ } else if (!isDir || path === currPath) {
63
+ this.opts.logger.info(`file removed: ${path}`);
64
+ this.notify({ id: EVENT_REMOVED, value: path });
65
+ this.remove(path);
66
+ } else {
67
+ const id = existsSync(currPath) ? EVENT_ADDED : EVENT_REMOVED;
68
+ this.opts.logger.info(`file ${id}: ${path}`);
69
+ this.notify({ id, value: currPath });
70
+ }
71
+ }, this.opts.delay);
72
+ }
73
+ );
74
+ return true;
75
+ }
76
+ remove(path) {
77
+ const watcher = this.watchers[path];
78
+ if (!watcher)
79
+ return false;
80
+ this.opts.logger.debug(`removing watcher for: ${path}`);
81
+ watcher.close();
82
+ delete this.watchers[path];
83
+ return true;
84
+ }
85
+ removeAll(paths) {
86
+ for (let p of paths) {
87
+ this.remove(p);
88
+ }
89
+ }
90
+ clear() {
91
+ this.removeAll(Object.keys(this.watchers));
92
+ }
93
+ installSignalHandler() {
94
+ if (!this._signal) {
95
+ process.on("SIGINT", () => this.clear());
96
+ this._signal = true;
97
+ }
98
+ }
99
+ // @ts-ignore mixin
100
+ addListener(id, fn, scope) {
101
+ }
102
+ // @ts-ignore mixin
103
+ removeListener(id, fn, scope) {
104
+ }
105
+ // @ts-ignore mixin
106
+ notify(event) {
107
+ }
108
+ };
109
+ Watcher = __decorateClass([
110
+ INotifyMixin
111
+ ], Watcher);
112
+ const fileWatcher = (opts) => new Watcher(opts);
113
+ export {
114
+ EVENT_ADDED,
115
+ EVENT_CHANGED,
116
+ EVENT_REMOVED,
117
+ Watcher,
118
+ fileWatcher
119
+ };