@thi.ng/file-io 1.1.4 → 1.2.0

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-31T09:44:23Z
3
+ - **Last updated**: 2024-01-26T18:03:04Z
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
@@ -45,7 +45,7 @@ For Node.js REPL:
45
45
  const fileIo = await import("@thi.ng/file-io");
46
46
  ```
47
47
 
48
- Package sizes (brotli'd, pre-treeshake): ESM: 1.30 KB
48
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.98 KB
49
49
 
50
50
  ## Dependencies
51
51
 
@@ -78,4 +78,4 @@ If this project contributes to an academic publication, please cite it as:
78
78
 
79
79
  ## License
80
80
 
81
- © 2022 - 2023 Karsten Schmidt // Apache License 2.0
81
+ © 2022 - 2024 Karsten Schmidt // Apache License 2.0
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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/file-io",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
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.16",
39
- "@thi.ng/checks": "^3.4.16",
40
- "@thi.ng/hex": "^2.3.28",
41
- "@thi.ng/logger": "^2.1.3",
42
- "@thi.ng/random": "^3.6.23"
38
+ "@thi.ng/api": "^8.9.18",
39
+ "@thi.ng/checks": "^3.4.18",
40
+ "@thi.ng/hex": "^2.3.30",
41
+ "@thi.ng/logger": "^2.1.4",
42
+ "@thi.ng/random": "^3.6.25"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@microsoft/api-extractor": "^7.39.0",
@@ -108,6 +108,9 @@
108
108
  "./text": {
109
109
  "default": "./text.js"
110
110
  },
111
+ "./watch": {
112
+ "default": "./watch.js"
113
+ },
111
114
  "./write": {
112
115
  "default": "./write.js"
113
116
  }
@@ -116,5 +119,5 @@
116
119
  "status": "stable",
117
120
  "year": 2022
118
121
  },
119
- "gitHead": "b3db173682e1148cf08a6bd907b8d90b47b7c066\n"
122
+ "gitHead": "7426e2ae6fca5482c6eaf649872296fc89955374\n"
120
123
  }
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
+ };