@gjsify/fs 0.3.13 → 0.3.14

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.
@@ -1,171 +1,163 @@
1
+ import { normalizePath } from "./utils.js";
1
2
  import GLib from "@girs/glib-2.0";
2
3
  import Gio from "@girs/gio-2.0";
3
4
  import { EventEmitter } from "node:events";
4
- import { normalizePath } from "./utils.js";
5
- const privates = /* @__PURE__ */ new WeakMap();
6
- class FSWatcher extends EventEmitter {
7
- constructor(filename, options, listener) {
8
- super();
9
- if (!options || typeof options !== "object")
10
- options = { persistent: true };
11
- const persistent = options.persistent !== false;
12
- const cancellable = Gio.Cancellable.new();
13
- const pathStr = normalizePath(filename);
14
- const file = Gio.File.new_for_path(pathStr);
15
- const watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
16
- watcher.connect("changed", changed.bind(this));
17
- let sourceId = null;
18
- if (persistent) {
19
- sourceId = GLib.timeout_add(
20
- GLib.PRIORITY_LOW,
21
- 2147483647,
22
- () => GLib.SOURCE_CONTINUE
23
- );
24
- }
25
- privates.set(this, {
26
- persistent,
27
- cancellable,
28
- sourceId,
29
- // even if never used later on, the monitor needs to be
30
- // attached to this instance or GJS reference counter
31
- // will ignore it and no watch will ever happen
32
- watcher
33
- });
34
- if (listener) this.on("change", listener);
35
- }
36
- close() {
37
- const priv = privates.get(this);
38
- if (!priv.cancellable.is_cancelled()) {
39
- priv.cancellable.cancel();
40
- if (priv.sourceId !== null) {
41
- GLib.source_remove(priv.sourceId);
42
- priv.sourceId = null;
43
- }
44
- }
45
- }
46
- /**
47
- * When called, requests that the Node.js event loop not exit so long as the
48
- * FSWatcher is active. Calling ref() multiple times has no effect.
49
- */
50
- ref() {
51
- const priv = privates.get(this);
52
- if (!priv.persistent && !priv.cancellable.is_cancelled()) {
53
- priv.persistent = true;
54
- priv.sourceId = GLib.timeout_add(
55
- GLib.PRIORITY_LOW,
56
- 2147483647,
57
- () => GLib.SOURCE_CONTINUE
58
- );
59
- }
60
- return this;
61
- }
62
- /**
63
- * When called, the active FSWatcher will not require the Node.js event loop
64
- * to remain active. Calling unref() multiple times has no effect.
65
- */
66
- unref() {
67
- const priv = privates.get(this);
68
- if (priv.persistent) {
69
- priv.persistent = false;
70
- if (priv.sourceId !== null) {
71
- GLib.source_remove(priv.sourceId);
72
- priv.sourceId = null;
73
- }
74
- }
75
- return this;
76
- }
77
- }
5
+
6
+ //#region src/fs-watcher.ts
7
+ const privates = new WeakMap();
8
+ var FSWatcher = class extends EventEmitter {
9
+ constructor(filename, options, listener) {
10
+ super();
11
+ if (!options || typeof options !== "object") options = { persistent: true };
12
+ const persistent = options.persistent !== false;
13
+ const cancellable = Gio.Cancellable.new();
14
+ const pathStr = normalizePath(filename);
15
+ const file = Gio.File.new_for_path(pathStr);
16
+ const watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
17
+ watcher.connect("changed", changed.bind(this));
18
+ let sourceId = null;
19
+ if (persistent) {
20
+ sourceId = GLib.timeout_add(GLib.PRIORITY_LOW, 2147483647, () => GLib.SOURCE_CONTINUE);
21
+ }
22
+ privates.set(this, {
23
+ persistent,
24
+ cancellable,
25
+ sourceId,
26
+ watcher
27
+ });
28
+ if (listener) this.on("change", listener);
29
+ }
30
+ close() {
31
+ const priv = privates.get(this);
32
+ if (!priv.cancellable.is_cancelled()) {
33
+ priv.cancellable.cancel();
34
+ if (priv.sourceId !== null) {
35
+ GLib.source_remove(priv.sourceId);
36
+ priv.sourceId = null;
37
+ }
38
+ }
39
+ }
40
+ /**
41
+ * When called, requests that the Node.js event loop not exit so long as the
42
+ * FSWatcher is active. Calling ref() multiple times has no effect.
43
+ */
44
+ ref() {
45
+ const priv = privates.get(this);
46
+ if (!priv.persistent && !priv.cancellable.is_cancelled()) {
47
+ priv.persistent = true;
48
+ priv.sourceId = GLib.timeout_add(GLib.PRIORITY_LOW, 2147483647, () => GLib.SOURCE_CONTINUE);
49
+ }
50
+ return this;
51
+ }
52
+ /**
53
+ * When called, the active FSWatcher will not require the Node.js event loop
54
+ * to remain active. Calling unref() multiple times has no effect.
55
+ */
56
+ unref() {
57
+ const priv = privates.get(this);
58
+ if (priv.persistent) {
59
+ priv.persistent = false;
60
+ if (priv.sourceId !== null) {
61
+ GLib.source_remove(priv.sourceId);
62
+ priv.sourceId = null;
63
+ }
64
+ }
65
+ return this;
66
+ }
67
+ };
78
68
  ;
79
69
  function changed(watcher, file, otherFile, eventType) {
80
- switch (eventType) {
81
- case Gio.FileMonitorEvent.CHANGES_DONE_HINT:
82
- this.emit("change", "change", file.get_basename());
83
- break;
84
- case Gio.FileMonitorEvent.DELETED:
85
- case Gio.FileMonitorEvent.CREATED:
86
- case Gio.FileMonitorEvent.RENAMED:
87
- case Gio.FileMonitorEvent.MOVED_IN:
88
- case Gio.FileMonitorEvent.MOVED_OUT:
89
- this.emit("rename", "rename", file.get_basename());
90
- break;
91
- }
70
+ switch (eventType) {
71
+ case Gio.FileMonitorEvent.CHANGES_DONE_HINT:
72
+ this.emit("change", "change", file.get_basename());
73
+ break;
74
+ case Gio.FileMonitorEvent.DELETED:
75
+ case Gio.FileMonitorEvent.CREATED:
76
+ case Gio.FileMonitorEvent.RENAMED:
77
+ case Gio.FileMonitorEvent.MOVED_IN:
78
+ case Gio.FileMonitorEvent.MOVED_OUT:
79
+ this.emit("rename", "rename", file.get_basename());
80
+ break;
81
+ }
92
82
  }
93
- var fs_watcher_default = FSWatcher;
94
83
  function gioEventToNodeType(eventType) {
95
- switch (eventType) {
96
- case Gio.FileMonitorEvent.CHANGES_DONE_HINT:
97
- return "change";
98
- case Gio.FileMonitorEvent.DELETED:
99
- case Gio.FileMonitorEvent.CREATED:
100
- case Gio.FileMonitorEvent.RENAMED:
101
- case Gio.FileMonitorEvent.MOVED_IN:
102
- case Gio.FileMonitorEvent.MOVED_OUT:
103
- return "rename";
104
- default:
105
- return null;
106
- }
84
+ switch (eventType) {
85
+ case Gio.FileMonitorEvent.CHANGES_DONE_HINT: return "change";
86
+ case Gio.FileMonitorEvent.DELETED:
87
+ case Gio.FileMonitorEvent.CREATED:
88
+ case Gio.FileMonitorEvent.RENAMED:
89
+ case Gio.FileMonitorEvent.MOVED_IN:
90
+ case Gio.FileMonitorEvent.MOVED_OUT: return "rename";
91
+ default: return null;
92
+ }
107
93
  }
108
94
  async function* watchAsync(filename, options) {
109
- const signal = options?.signal;
110
- if (signal?.aborted) return;
111
- const pathStr = normalizePath(filename);
112
- const file = Gio.File.new_for_path(pathStr);
113
- const cancellable = Gio.Cancellable.new();
114
- let watcher;
115
- try {
116
- watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
117
- } catch {
118
- return;
119
- }
120
- const eventQueue = [];
121
- const waiterQueue = [];
122
- let finished = false;
123
- function enqueue(event) {
124
- if (finished) return;
125
- if (waiterQueue.length > 0) {
126
- waiterQueue.shift().resolve({ value: event, done: false });
127
- } else {
128
- eventQueue.push(event);
129
- }
130
- }
131
- function terminate() {
132
- if (finished) return;
133
- finished = true;
134
- if (!cancellable.is_cancelled()) cancellable.cancel();
135
- while (waiterQueue.length > 0) {
136
- waiterQueue.shift().resolve({ value: void 0, done: true });
137
- }
138
- }
139
- const signalId = watcher.connect("changed", (_mon, changedFile, _otherFile, eventType) => {
140
- const type = gioEventToNodeType(eventType);
141
- if (type === null) return;
142
- enqueue({ eventType: type, filename: changedFile?.get_basename() ?? null });
143
- });
144
- const abortHandler = () => terminate();
145
- signal?.addEventListener("abort", abortHandler);
146
- try {
147
- while (!finished) {
148
- if (eventQueue.length > 0) {
149
- yield eventQueue.shift();
150
- continue;
151
- }
152
- const result = await new Promise((resolve) => {
153
- waiterQueue.push({ resolve });
154
- });
155
- if (result.done) break;
156
- yield result.value;
157
- }
158
- } finally {
159
- signal?.removeEventListener("abort", abortHandler);
160
- try {
161
- watcher.disconnect(signalId);
162
- } catch {
163
- }
164
- if (!cancellable.is_cancelled()) cancellable.cancel();
165
- }
95
+ const signal = options?.signal;
96
+ if (signal?.aborted) return;
97
+ const pathStr = normalizePath(filename);
98
+ const file = Gio.File.new_for_path(pathStr);
99
+ const cancellable = Gio.Cancellable.new();
100
+ let watcher;
101
+ try {
102
+ watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
103
+ } catch {
104
+ return;
105
+ }
106
+ const eventQueue = [];
107
+ const waiterQueue = [];
108
+ let finished = false;
109
+ function enqueue(event) {
110
+ if (finished) return;
111
+ if (waiterQueue.length > 0) {
112
+ waiterQueue.shift().resolve({
113
+ value: event,
114
+ done: false
115
+ });
116
+ } else {
117
+ eventQueue.push(event);
118
+ }
119
+ }
120
+ function terminate() {
121
+ if (finished) return;
122
+ finished = true;
123
+ if (!cancellable.is_cancelled()) cancellable.cancel();
124
+ while (waiterQueue.length > 0) {
125
+ waiterQueue.shift().resolve({
126
+ value: undefined,
127
+ done: true
128
+ });
129
+ }
130
+ }
131
+ const signalId = watcher.connect("changed", (_mon, changedFile, _otherFile, eventType) => {
132
+ const type = gioEventToNodeType(eventType);
133
+ if (type === null) return;
134
+ enqueue({
135
+ eventType: type,
136
+ filename: changedFile?.get_basename() ?? null
137
+ });
138
+ });
139
+ const abortHandler = () => terminate();
140
+ signal?.addEventListener("abort", abortHandler);
141
+ try {
142
+ while (!finished) {
143
+ if (eventQueue.length > 0) {
144
+ yield eventQueue.shift();
145
+ continue;
146
+ }
147
+ const result = await new Promise((resolve) => {
148
+ waiterQueue.push({ resolve });
149
+ });
150
+ if (result.done) break;
151
+ yield result.value;
152
+ }
153
+ } finally {
154
+ signal?.removeEventListener("abort", abortHandler);
155
+ try {
156
+ watcher.disconnect(signalId);
157
+ } catch {}
158
+ if (!cancellable.is_cancelled()) cancellable.cancel();
159
+ }
166
160
  }
167
- export {
168
- FSWatcher,
169
- fs_watcher_default as default,
170
- watchAsync
171
- };
161
+
162
+ //#endregion
163
+ export { FSWatcher, FSWatcher as default, watchAsync };
package/lib/esm/glob.js CHANGED
@@ -1,164 +1,163 @@
1
- import { readdirSync } from "./sync.js";
2
1
  import { normalizePath } from "./utils.js";
2
+ import { readdirSync } from "./sync.js";
3
+
4
+ //#region src/glob.ts
5
+ /** Convert a single glob segment (no `/`) to a regex source string */
3
6
  function segmentToRegexSrc(seg) {
4
- const extglob = /^([!*+?@])\((.+)\)$/.exec(seg);
5
- if (extglob) {
6
- const [, type, inner] = extglob;
7
- const parts = inner.split("|").map((p) => segmentToRegexSrc(p));
8
- const group = "(?:" + parts.join("|") + ")";
9
- switch (type) {
10
- case "!":
11
- return "(?!(?:" + parts.join("|") + "))[^/]*";
12
- case "*":
13
- return group + "*";
14
- case "+":
15
- return group + "+";
16
- case "?":
17
- return group + "?";
18
- case "@":
19
- return group;
20
- }
21
- }
22
- let result = "";
23
- let i = 0;
24
- while (i < seg.length) {
25
- const c = seg[i];
26
- if (c === "*") {
27
- result += "[^/]*";
28
- i++;
29
- continue;
30
- }
31
- if (c === "?") {
32
- result += "[^/]";
33
- i++;
34
- continue;
35
- }
36
- if (c === "[") {
37
- const end = seg.indexOf("]", i + 1);
38
- if (end === -1) {
39
- result += "\\[";
40
- i++;
41
- continue;
42
- }
43
- result += seg.slice(i, end + 1);
44
- i = end + 1;
45
- continue;
46
- }
47
- if (c === "{") {
48
- const end = seg.indexOf("}", i + 1);
49
- if (end === -1) {
50
- result += "\\{";
51
- i++;
52
- continue;
53
- }
54
- const alts = seg.slice(i + 1, end).split(",").map((a) => segmentToRegexSrc(a.trim()));
55
- result += "(?:" + alts.join("|") + ")";
56
- i = end + 1;
57
- continue;
58
- }
59
- if (".+^$|()[]{}".includes(c)) {
60
- result += "\\" + c;
61
- } else {
62
- result += c;
63
- }
64
- i++;
65
- }
66
- return result;
7
+ const extglob = /^([!*+?@])\((.+)\)$/.exec(seg);
8
+ if (extglob) {
9
+ const [, type, inner] = extglob;
10
+ const parts = inner.split("|").map((p) => segmentToRegexSrc(p));
11
+ const group = "(?:" + parts.join("|") + ")";
12
+ switch (type) {
13
+ case "!": return "(?!(?:" + parts.join("|") + "))[^/]*";
14
+ case "*": return group + "*";
15
+ case "+": return group + "+";
16
+ case "?": return group + "?";
17
+ case "@": return group;
18
+ }
19
+ }
20
+ let result = "";
21
+ let i = 0;
22
+ while (i < seg.length) {
23
+ const c = seg[i];
24
+ if (c === "*") {
25
+ result += "[^/]*";
26
+ i++;
27
+ continue;
28
+ }
29
+ if (c === "?") {
30
+ result += "[^/]";
31
+ i++;
32
+ continue;
33
+ }
34
+ if (c === "[") {
35
+ const end = seg.indexOf("]", i + 1);
36
+ if (end === -1) {
37
+ result += "\\[";
38
+ i++;
39
+ continue;
40
+ }
41
+ result += seg.slice(i, end + 1);
42
+ i = end + 1;
43
+ continue;
44
+ }
45
+ if (c === "{") {
46
+ const end = seg.indexOf("}", i + 1);
47
+ if (end === -1) {
48
+ result += "\\{";
49
+ i++;
50
+ continue;
51
+ }
52
+ const alts = seg.slice(i + 1, end).split(",").map((a) => segmentToRegexSrc(a.trim()));
53
+ result += "(?:" + alts.join("|") + ")";
54
+ i = end + 1;
55
+ continue;
56
+ }
57
+ if (".+^$|()[]{}".includes(c)) {
58
+ result += "\\" + c;
59
+ } else {
60
+ result += c;
61
+ }
62
+ i++;
63
+ }
64
+ return result;
67
65
  }
66
+ /**
67
+ * Convert a glob pattern to a RegExp that matches relative POSIX paths.
68
+ */
68
69
  function globToRegex(pattern) {
69
- pattern = pattern.replace(/\/+/g, "/").replace(/^\.\//, "");
70
- const segments = pattern.split("/");
71
- const parts = [];
72
- for (let si = 0; si < segments.length; si++) {
73
- const seg = segments[si];
74
- const isLast = si === segments.length - 1;
75
- if (seg === "**") {
76
- if (isLast) {
77
- if (parts.length > 0 && parts[parts.length - 1] === "/") {
78
- parts.pop();
79
- parts.push("(?:/.+)?");
80
- } else {
81
- parts.push("(?:.+)?");
82
- }
83
- } else {
84
- parts.push("(?:[^/]+/)*");
85
- }
86
- } else {
87
- parts.push(segmentToRegexSrc(seg));
88
- if (!isLast) parts.push("/");
89
- }
90
- }
91
- return new RegExp("^(?:" + parts.join("") + ")$");
70
+ pattern = pattern.replace(/\/+/g, "/").replace(/^\.\//, "");
71
+ const segments = pattern.split("/");
72
+ const parts = [];
73
+ for (let si = 0; si < segments.length; si++) {
74
+ const seg = segments[si];
75
+ const isLast = si === segments.length - 1;
76
+ if (seg === "**") {
77
+ if (isLast) {
78
+ if (parts.length > 0 && parts[parts.length - 1] === "/") {
79
+ parts.pop();
80
+ parts.push("(?:/.+)?");
81
+ } else {
82
+ parts.push("(?:.+)?");
83
+ }
84
+ } else {
85
+ parts.push("(?:[^/]+/)*");
86
+ }
87
+ } else {
88
+ parts.push(segmentToRegexSrc(seg));
89
+ if (!isLast) parts.push("/");
90
+ }
91
+ }
92
+ return new RegExp("^(?:" + parts.join("") + ")$");
92
93
  }
93
94
  function buildExcludePredicate(exclude) {
94
- if (!exclude) return null;
95
- if (typeof exclude === "function") return exclude;
96
- const patterns = Array.isArray(exclude) ? exclude : [exclude];
97
- const regexes = patterns.map((p) => globToRegex(p));
98
- return (path) => regexes.some((rx) => rx.test(path));
95
+ if (!exclude) return null;
96
+ if (typeof exclude === "function") return exclude;
97
+ const patterns = Array.isArray(exclude) ? exclude : [exclude];
98
+ const regexes = patterns.map((p) => globToRegex(p));
99
+ return (path) => regexes.some((rx) => rx.test(path));
99
100
  }
100
101
  function matchAll(pattern, cwd, exclude) {
101
- const regex = globToRegex(pattern);
102
- const isExcluded = buildExcludePredicate(exclude);
103
- let allEntries;
104
- try {
105
- allEntries = readdirSync(cwd, { recursive: true });
106
- } catch {
107
- return [];
108
- }
109
- const candidates = [".", ...allEntries];
110
- const results = [];
111
- for (const entry of candidates) {
112
- const normalized = entry.replace(/\\/g, "/");
113
- if (!regex.test(normalized)) continue;
114
- if (isExcluded && isExcluded(normalized)) continue;
115
- results.push(entry);
116
- }
117
- return results;
102
+ const regex = globToRegex(pattern);
103
+ const isExcluded = buildExcludePredicate(exclude);
104
+ let allEntries;
105
+ try {
106
+ allEntries = readdirSync(cwd, { recursive: true });
107
+ } catch {
108
+ return [];
109
+ }
110
+ const candidates = [".", ...allEntries];
111
+ const results = [];
112
+ for (const entry of candidates) {
113
+ const normalized = entry.replace(/\\/g, "/");
114
+ if (!regex.test(normalized)) continue;
115
+ if (isExcluded && isExcluded(normalized)) continue;
116
+ results.push(entry);
117
+ }
118
+ return results;
118
119
  }
119
120
  function globSync(pattern, options) {
120
- const patterns = Array.isArray(pattern) ? pattern : [pattern];
121
- const cwd = options?.cwd ? normalizePath(options.cwd) : globalThis.process?.cwd?.() ?? "/";
122
- const exclude = options?.exclude;
123
- const seen = /* @__PURE__ */ new Set();
124
- const results = [];
125
- for (const p of patterns) {
126
- for (const match of matchAll(p, cwd, exclude)) {
127
- if (!seen.has(match)) {
128
- seen.add(match);
129
- results.push(match);
130
- }
131
- }
132
- }
133
- return results;
121
+ const patterns = Array.isArray(pattern) ? pattern : [pattern];
122
+ const cwd = options?.cwd ? normalizePath(options.cwd) : globalThis.process?.cwd?.() ?? "/";
123
+ const exclude = options?.exclude;
124
+ const seen = new Set();
125
+ const results = [];
126
+ for (const p of patterns) {
127
+ for (const match of matchAll(p, cwd, exclude)) {
128
+ if (!seen.has(match)) {
129
+ seen.add(match);
130
+ results.push(match);
131
+ }
132
+ }
133
+ }
134
+ return results;
134
135
  }
135
136
  function glob(pattern, options, callback) {
136
- let opts;
137
- let cb;
138
- if (typeof options === "function") {
139
- cb = options;
140
- opts = {};
141
- } else {
142
- cb = callback;
143
- opts = options || {};
144
- }
145
- Promise.resolve().then(() => {
146
- try {
147
- const matches = globSync(pattern, opts);
148
- cb(null, matches);
149
- } catch (err) {
150
- cb(err, []);
151
- }
152
- });
137
+ let opts;
138
+ let cb;
139
+ if (typeof options === "function") {
140
+ cb = options;
141
+ opts = {};
142
+ } else {
143
+ cb = callback;
144
+ opts = options || {};
145
+ }
146
+ Promise.resolve().then(() => {
147
+ try {
148
+ const matches = globSync(pattern, opts);
149
+ cb(null, matches);
150
+ } catch (err) {
151
+ cb(err, []);
152
+ }
153
+ });
153
154
  }
154
155
  async function* globAsync(pattern, options) {
155
- const matches = globSync(pattern, options);
156
- for (const m of matches) {
157
- yield m;
158
- }
156
+ const matches = globSync(pattern, options);
157
+ for (const m of matches) {
158
+ yield m;
159
+ }
159
160
  }
160
- export {
161
- glob,
162
- globAsync,
163
- globSync
164
- };
161
+
162
+ //#endregion
163
+ export { glob, globAsync, globSync };