utilium 0.4.4 → 0.5.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/dist/fs.d.ts ADDED
@@ -0,0 +1,70 @@
1
+ type fs = typeof import('fs');
2
+ declare let fs: fs;
3
+ export declare function whenDefaultFSDone(): Promise<void>;
4
+ export declare function useFS(_fs: fs): void;
5
+ export declare abstract class FileMap<V> implements Map<string, V> {
6
+ protected readonly path: string;
7
+ get [Symbol.toStringTag](): string;
8
+ constructor(path: string);
9
+ protected abstract readonly _map: Map<string, V>;
10
+ abstract clear(): void;
11
+ abstract delete(key: string): boolean;
12
+ abstract get(key: string): V;
13
+ abstract has(key: string): boolean;
14
+ abstract set(key: string, value: V): this;
15
+ get size(): number;
16
+ get [Symbol.iterator](): () => IterableIterator<[string, V]>;
17
+ get keys(): typeof this._map.keys;
18
+ get values(): typeof this._map.values;
19
+ get entries(): typeof this._map.entries;
20
+ get forEach(): typeof this._map.forEach;
21
+ }
22
+ export type JSONObject<Key extends string | number | symbol = string> = {
23
+ [K in Key]: JSONValue;
24
+ };
25
+ export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
26
+ export interface JSONFileMapOptions {
27
+ /**
28
+ * Should an invalid JSON file be overwritten
29
+ */
30
+ overwrite_invalid: boolean;
31
+ }
32
+ /**
33
+ * A Map overlaying a JSON file
34
+ */
35
+ export declare class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
36
+ readonly options: JSONFileMapOptions;
37
+ get [Symbol.toStringTag](): 'JSONFileMap';
38
+ constructor(path: string, options: JSONFileMapOptions);
39
+ get _map(): Map<string, T>;
40
+ _write(map: Map<string, T>): void;
41
+ clear(): void;
42
+ delete(key: string): boolean;
43
+ get<U extends T>(key: string): U;
44
+ has(key: string): boolean;
45
+ set(key: string, value: T): this;
46
+ }
47
+ export interface FolderMapOptions {
48
+ /**
49
+ * Suffix to append to keys to resolve file names
50
+ */
51
+ suffix: string;
52
+ }
53
+ /**
54
+ * A Map overlaying a folder
55
+ */
56
+ export declare class FolderMap extends FileMap<string> {
57
+ readonly options: Partial<FolderMapOptions>;
58
+ get [Symbol.toStringTag](): 'FolderMap';
59
+ constructor(path: string, options: Partial<FolderMapOptions>);
60
+ protected get _names(): string[];
61
+ protected _join(path: string): string;
62
+ protected get _map(): Map<string, string>;
63
+ clear(): void;
64
+ delete(key: string): boolean;
65
+ get(key: string): string;
66
+ has(key: string): boolean;
67
+ set(key: string, value: string): this;
68
+ }
69
+ export declare function gitCommitHash(repo?: string): string;
70
+ export {};
package/dist/fs.js ADDED
@@ -0,0 +1,169 @@
1
+ import { isJSON } from './objects.js';
2
+ let fs;
3
+ const defaultFS = import('fs').then(_ => (fs = _));
4
+ export async function whenDefaultFSDone() {
5
+ await defaultFS;
6
+ }
7
+ export function useFS(_fs) {
8
+ fs = _fs;
9
+ }
10
+ export class FileMap {
11
+ path;
12
+ get [Symbol.toStringTag]() {
13
+ return 'FileMap';
14
+ }
15
+ constructor(path) {
16
+ this.path = path;
17
+ if (!path) {
18
+ throw new ReferenceError('No path specified');
19
+ }
20
+ if (!fs) {
21
+ throw new ReferenceError('No filesystem API');
22
+ }
23
+ }
24
+ get size() {
25
+ return this._map.size;
26
+ }
27
+ get [Symbol.iterator]() {
28
+ return this._map[Symbol.iterator].bind(this._map);
29
+ }
30
+ get keys() {
31
+ return this._map.keys.bind(this._map);
32
+ }
33
+ get values() {
34
+ return this._map.values.bind(this._map);
35
+ }
36
+ get entries() {
37
+ return this._map.entries.bind(this._map);
38
+ }
39
+ get forEach() {
40
+ return this._map.forEach.bind(this._map);
41
+ }
42
+ }
43
+ /**
44
+ * A Map overlaying a JSON file
45
+ */
46
+ export class JSONFileMap extends FileMap {
47
+ options;
48
+ get [Symbol.toStringTag]() {
49
+ return 'JSONFileMap';
50
+ }
51
+ constructor(path, options) {
52
+ super(path);
53
+ this.options = options;
54
+ if (!fs.existsSync(path)) {
55
+ fs.writeFileSync(path, '{}');
56
+ }
57
+ }
58
+ get _map() {
59
+ const content = fs.readFileSync(this.path, 'utf8');
60
+ if (!isJSON(content)) {
61
+ if (!this.options.overwrite_invalid) {
62
+ throw new SyntaxError('Invalid JSON file: ' + this.path);
63
+ }
64
+ console.warn('Invalid JSON file (overwriting): ' + this.path);
65
+ this.clear();
66
+ return new Map();
67
+ }
68
+ return new Map(Object.entries(JSON.parse(content)));
69
+ }
70
+ _write(map) {
71
+ if (!fs.existsSync(this.path)) {
72
+ fs.writeFileSync(this.path, '{}');
73
+ }
74
+ const content = JSON.stringify(Object.fromEntries(map));
75
+ fs.writeFileSync(this.path, content);
76
+ }
77
+ clear() {
78
+ fs.writeFileSync(this.path, '{}');
79
+ }
80
+ delete(key) {
81
+ const map = this._map;
82
+ const rt = map.delete(key);
83
+ this._write(map);
84
+ return rt;
85
+ }
86
+ get(key) {
87
+ return this._map.get(key);
88
+ }
89
+ has(key) {
90
+ return this._map.has(key);
91
+ }
92
+ set(key, value) {
93
+ const map = this._map;
94
+ map.set(key, value);
95
+ this._write(map);
96
+ return this;
97
+ }
98
+ }
99
+ /**
100
+ * A Map overlaying a folder
101
+ */
102
+ export class FolderMap extends FileMap {
103
+ options;
104
+ get [Symbol.toStringTag]() {
105
+ return 'FolderMap';
106
+ }
107
+ constructor(path, options) {
108
+ super(path);
109
+ this.options = options;
110
+ }
111
+ get _names() {
112
+ return fs
113
+ .readdirSync(this.path)
114
+ .filter(p => p.endsWith(this.options.suffix || ''))
115
+ .map(p => p.slice(0, -this.options.suffix.length));
116
+ }
117
+ _join(path) {
118
+ return `${this.path}/${path}${this.options.suffix}`;
119
+ }
120
+ get _map() {
121
+ const entries = [];
122
+ for (const name of this._names) {
123
+ const content = fs.readFileSync(this._join(name), 'utf8');
124
+ entries.push([name, content]);
125
+ }
126
+ return new Map(entries);
127
+ }
128
+ clear() {
129
+ for (const name of this._names) {
130
+ fs.unlinkSync(this._join(name));
131
+ }
132
+ }
133
+ delete(key) {
134
+ if (!this.has(key)) {
135
+ return false;
136
+ }
137
+ fs.unlinkSync(this._join(key));
138
+ return true;
139
+ }
140
+ get(key) {
141
+ if (!this.has(key)) {
142
+ throw new ReferenceError('Key not found');
143
+ }
144
+ return fs.readFileSync(this._join(key), 'utf8');
145
+ }
146
+ has(key) {
147
+ return this._names.includes(key);
148
+ }
149
+ set(key, value) {
150
+ fs.writeFileSync(this._join(key), value);
151
+ return this;
152
+ }
153
+ }
154
+ export function gitCommitHash(repo = '.') {
155
+ repo = repo.replaceAll(/\/+/, '/').replaceAll(/\/$/, '');
156
+ const rev = fs
157
+ .readFileSync(repo + '.git/HEAD')
158
+ .toString()
159
+ .trim();
160
+ if (rev.indexOf(':') === -1) {
161
+ return rev;
162
+ }
163
+ else {
164
+ return fs
165
+ .readFileSync(repo + '.git/' + rev.substring(5))
166
+ .toString()
167
+ .trim();
168
+ }
169
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './fs.js';
1
2
  export * from './list.js';
2
3
  export * from './misc.js';
3
4
  export * from './numbers.js';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './fs.js';
1
2
  export * from './list.js';
2
3
  export * from './misc.js';
3
4
  export * from './numbers.js';
package/dist/objects.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- import type * as FS from 'fs';
3
1
  import { UnionToTuple } from './types.js';
4
2
  export declare function filterObject<O extends object, R extends object>(object: O, predicate: (key: keyof O, value: O[keyof O]) => boolean): R;
5
3
  export declare function pick<T extends object, K extends keyof T>(object: T, ...keys: readonly K[]): Pick<T, K>;
@@ -14,74 +12,4 @@ export type Entries<T extends object> = UnionToTuple<{
14
12
  [K in keyof T]: [K, T[K]];
15
13
  }[keyof T]>;
16
14
  export declare function isJSON(str: string): boolean;
17
- export declare abstract class FileMap<V> implements Map<string, V> {
18
- protected readonly path: string;
19
- protected fs: typeof FS;
20
- get [Symbol.toStringTag](): string;
21
- constructor(path: string, fs: typeof FS);
22
- protected abstract readonly _map: Map<string, V>;
23
- abstract clear(): void;
24
- abstract delete(key: string): boolean;
25
- abstract get(key: string): V;
26
- abstract has(key: string): boolean;
27
- abstract set(key: string, value: V): this;
28
- get size(): number;
29
- get [Symbol.iterator](): () => IterableIterator<[string, V]>;
30
- get keys(): typeof this._map.keys;
31
- get values(): typeof this._map.values;
32
- get entries(): typeof this._map.entries;
33
- get forEach(): typeof this._map.forEach;
34
- }
35
- export type JSONObject<Key extends string | number | symbol = string> = {
36
- [K in Key]: JSONValue;
37
- };
38
- export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
39
- export interface JSONFileMapOptions {
40
- /**
41
- * Should an invalid JSON file be overwritten
42
- */
43
- overwrite_invalid_json: boolean;
44
- /**
45
- * FS module
46
- */
47
- fs: typeof FS;
48
- }
49
- /**
50
- * A Map overlaying a JSON file
51
- */
52
- export declare class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
53
- readonly options: JSONFileMapOptions;
54
- get [Symbol.toStringTag](): 'JSONFileMap';
55
- constructor(path: string, options: JSONFileMapOptions);
56
- get _map(): Map<string, T>;
57
- _write(map: Map<string, T>): void;
58
- clear(): void;
59
- delete(key: string): boolean;
60
- get<U extends T>(key: string): U;
61
- has(key: string): boolean;
62
- set(key: string, value: T): this;
63
- }
64
- export interface FolderMapOptions {
65
- /**
66
- * Suffix to append to keys to resolve file names
67
- */
68
- suffix: string;
69
- fs: typeof FS;
70
- }
71
- /**
72
- * A Map overlaying a folder
73
- */
74
- export declare class FolderMap extends FileMap<string> {
75
- readonly options: Partial<FolderMapOptions>;
76
- get [Symbol.toStringTag](): 'FolderMap';
77
- constructor(path: string, options: Partial<FolderMapOptions>);
78
- protected get _names(): string[];
79
- protected _join(path: string): string;
80
- protected get _map(): Map<string, string>;
81
- clear(): void;
82
- delete(key: string): boolean;
83
- get(key: string): string;
84
- has(key: string): boolean;
85
- set(key: string, value: string): this;
86
- }
87
15
  export declare function resolveConstructors(object: object): string[];
package/dist/objects.js CHANGED
@@ -32,152 +32,6 @@ export function isJSON(str) {
32
32
  return false;
33
33
  }
34
34
  }
35
- export class FileMap {
36
- path;
37
- fs;
38
- get [Symbol.toStringTag]() {
39
- return 'FileMap';
40
- }
41
- constructor(path, fs) {
42
- this.path = path;
43
- this.fs = fs;
44
- if (!path) {
45
- throw new ReferenceError('No path specified');
46
- }
47
- if (!fs) {
48
- throw new ReferenceError('No filesystem API');
49
- }
50
- }
51
- get size() {
52
- return this._map.size;
53
- }
54
- get [Symbol.iterator]() {
55
- return this._map[Symbol.iterator].bind(this._map);
56
- }
57
- get keys() {
58
- return this._map.keys.bind(this._map);
59
- }
60
- get values() {
61
- return this._map.values.bind(this._map);
62
- }
63
- get entries() {
64
- return this._map.entries.bind(this._map);
65
- }
66
- get forEach() {
67
- return this._map.forEach.bind(this._map);
68
- }
69
- }
70
- /**
71
- * A Map overlaying a JSON file
72
- */
73
- export class JSONFileMap extends FileMap {
74
- options;
75
- get [Symbol.toStringTag]() {
76
- return 'JSONFileMap';
77
- }
78
- constructor(path, options) {
79
- super(path, options.fs);
80
- this.options = options;
81
- if (!this.fs.existsSync(path)) {
82
- this.fs.writeFileSync(path, '{}');
83
- }
84
- }
85
- get _map() {
86
- const content = this.fs.readFileSync(this.path, 'utf8');
87
- if (!isJSON(content)) {
88
- if (!this.options.overwrite_invalid_json) {
89
- throw new SyntaxError('Invalid JSON file: ' + this.path);
90
- }
91
- console.warn('Invalid JSON file (overwriting): ' + this.path);
92
- this.clear();
93
- return new Map();
94
- }
95
- return new Map(Object.entries(JSON.parse(content)));
96
- }
97
- _write(map) {
98
- if (!this.fs.existsSync(this.path)) {
99
- this.fs.writeFileSync(this.path, '{}');
100
- }
101
- const content = JSON.stringify(Object.fromEntries(map));
102
- this.fs.writeFileSync(this.path, content);
103
- }
104
- clear() {
105
- this.fs.writeFileSync(this.path, '{}');
106
- }
107
- delete(key) {
108
- const map = this._map;
109
- const rt = map.delete(key);
110
- this._write(map);
111
- return rt;
112
- }
113
- get(key) {
114
- return this._map.get(key);
115
- }
116
- has(key) {
117
- return this._map.has(key);
118
- }
119
- set(key, value) {
120
- const map = this._map;
121
- map.set(key, value);
122
- this._write(map);
123
- return this;
124
- }
125
- }
126
- /**
127
- * A Map overlaying a folder
128
- */
129
- export class FolderMap extends FileMap {
130
- options;
131
- get [Symbol.toStringTag]() {
132
- return 'FolderMap';
133
- }
134
- constructor(path, options) {
135
- super(path, options.fs);
136
- this.options = options;
137
- }
138
- get _names() {
139
- return this.fs
140
- .readdirSync(this.path)
141
- .filter(p => p.endsWith(this.options.suffix || ''))
142
- .map(p => p.slice(0, -this.options.suffix.length));
143
- }
144
- _join(path) {
145
- return `${this.path}/${path}${this.options.suffix}`;
146
- }
147
- get _map() {
148
- const entries = [];
149
- for (const name of this._names) {
150
- const content = this.fs.readFileSync(this._join(name), 'utf8');
151
- entries.push([name, content]);
152
- }
153
- return new Map(entries);
154
- }
155
- clear() {
156
- for (const name of this._names) {
157
- this.fs.unlinkSync(this._join(name));
158
- }
159
- }
160
- delete(key) {
161
- if (!this.has(key)) {
162
- return false;
163
- }
164
- this.fs.unlinkSync(this._join(key));
165
- return true;
166
- }
167
- get(key) {
168
- if (!this.has(key)) {
169
- throw new ReferenceError('Key not found');
170
- }
171
- return this.fs.readFileSync(this._join(key), 'utf8');
172
- }
173
- has(key) {
174
- return this._names.includes(key);
175
- }
176
- set(key, value) {
177
- this.fs.writeFileSync(this._join(key), value);
178
- return this;
179
- }
180
- }
181
35
  export function resolveConstructors(object) {
182
36
  const constructors = [];
183
37
  let prototype = object;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utilium",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "Typescript utilies",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/fs.ts ADDED
@@ -0,0 +1,234 @@
1
+ import { isJSON } from './objects.js';
2
+
3
+ type fs = typeof import('fs');
4
+
5
+ let fs: fs;
6
+
7
+ const defaultFS = import('fs').then(_ => (fs = _));
8
+
9
+ export async function whenDefaultFSDone(): Promise<void> {
10
+ await defaultFS;
11
+ }
12
+
13
+ export function useFS(_fs: fs): void {
14
+ fs = _fs;
15
+ }
16
+
17
+ export abstract class FileMap<V> implements Map<string, V> {
18
+ public get [Symbol.toStringTag](): string {
19
+ return 'FileMap';
20
+ }
21
+
22
+ public constructor(protected readonly path: string) {
23
+ if (!path) {
24
+ throw new ReferenceError('No path specified');
25
+ }
26
+
27
+ if (!fs) {
28
+ throw new ReferenceError('No filesystem API');
29
+ }
30
+ }
31
+
32
+ protected abstract readonly _map: Map<string, V>;
33
+
34
+ public abstract clear(): void;
35
+
36
+ public abstract delete(key: string): boolean;
37
+
38
+ public abstract get(key: string): V;
39
+
40
+ public abstract has(key: string): boolean;
41
+
42
+ public abstract set(key: string, value: V): this;
43
+
44
+ public get size() {
45
+ return this._map.size;
46
+ }
47
+
48
+ public get [Symbol.iterator]() {
49
+ return this._map[Symbol.iterator].bind(this._map);
50
+ }
51
+
52
+ public get keys(): typeof this._map.keys {
53
+ return this._map.keys.bind(this._map);
54
+ }
55
+
56
+ public get values(): typeof this._map.values {
57
+ return this._map.values.bind(this._map);
58
+ }
59
+
60
+ public get entries(): typeof this._map.entries {
61
+ return this._map.entries.bind(this._map);
62
+ }
63
+
64
+ public get forEach(): typeof this._map.forEach {
65
+ return this._map.forEach.bind(this._map);
66
+ }
67
+ }
68
+
69
+ export type JSONObject<Key extends string | number | symbol = string> = { [K in Key]: JSONValue };
70
+
71
+ export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
72
+
73
+ export interface JSONFileMapOptions {
74
+ /**
75
+ * Should an invalid JSON file be overwritten
76
+ */
77
+ overwrite_invalid: boolean;
78
+ }
79
+
80
+ /**
81
+ * A Map overlaying a JSON file
82
+ */
83
+ export class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
84
+ public get [Symbol.toStringTag](): 'JSONFileMap' {
85
+ return 'JSONFileMap';
86
+ }
87
+
88
+ public constructor(
89
+ path: string,
90
+ public readonly options: JSONFileMapOptions
91
+ ) {
92
+ super(path);
93
+ if (!fs.existsSync(path)) {
94
+ fs.writeFileSync(path, '{}');
95
+ }
96
+ }
97
+
98
+ public get _map(): Map<string, T> {
99
+ const content = fs.readFileSync(this.path, 'utf8');
100
+ if (!isJSON(content)) {
101
+ if (!this.options.overwrite_invalid) {
102
+ throw new SyntaxError('Invalid JSON file: ' + this.path);
103
+ }
104
+ console.warn('Invalid JSON file (overwriting): ' + this.path);
105
+ this.clear();
106
+ return new Map();
107
+ }
108
+ return new Map(Object.entries(JSON.parse(content)));
109
+ }
110
+
111
+ public _write(map: Map<string, T>) {
112
+ if (!fs.existsSync(this.path)) {
113
+ fs.writeFileSync(this.path, '{}');
114
+ }
115
+ const content = JSON.stringify(Object.fromEntries(map));
116
+ fs.writeFileSync(this.path, content);
117
+ }
118
+
119
+ public clear() {
120
+ fs.writeFileSync(this.path, '{}');
121
+ }
122
+
123
+ public delete(key: string): boolean {
124
+ const map = this._map;
125
+ const rt = map.delete(key);
126
+ this._write(map);
127
+ return rt;
128
+ }
129
+
130
+ public get<U extends T>(key: string): U {
131
+ return this._map.get(key) as U;
132
+ }
133
+
134
+ public has(key: string): boolean {
135
+ return this._map.has(key);
136
+ }
137
+
138
+ public set(key: string, value: T): this {
139
+ const map = this._map;
140
+ map.set(key, value);
141
+ this._write(map);
142
+ return this;
143
+ }
144
+ }
145
+
146
+ export interface FolderMapOptions {
147
+ /**
148
+ * Suffix to append to keys to resolve file names
149
+ */
150
+ suffix: string;
151
+ }
152
+
153
+ /**
154
+ * A Map overlaying a folder
155
+ */
156
+ export class FolderMap extends FileMap<string> {
157
+ public get [Symbol.toStringTag](): 'FolderMap' {
158
+ return 'FolderMap';
159
+ }
160
+
161
+ public constructor(
162
+ path: string,
163
+ public readonly options: Partial<FolderMapOptions>
164
+ ) {
165
+ super(path);
166
+ }
167
+
168
+ protected get _names(): string[] {
169
+ return fs
170
+ .readdirSync(this.path)
171
+ .filter(p => p.endsWith(this.options.suffix || ''))
172
+ .map(p => p.slice(0, -this.options.suffix!.length));
173
+ }
174
+
175
+ protected _join(path: string): string {
176
+ return `${this.path}/${path}${this.options.suffix}`;
177
+ }
178
+
179
+ protected get _map(): Map<string, string> {
180
+ const entries: [string, string][] = [];
181
+ for (const name of this._names) {
182
+ const content = fs.readFileSync(this._join(name), 'utf8');
183
+ entries.push([name, content]);
184
+ }
185
+ return new Map(entries);
186
+ }
187
+
188
+ public clear(): void {
189
+ for (const name of this._names) {
190
+ fs.unlinkSync(this._join(name));
191
+ }
192
+ }
193
+
194
+ public delete(key: string): boolean {
195
+ if (!this.has(key)) {
196
+ return false;
197
+ }
198
+
199
+ fs.unlinkSync(this._join(key));
200
+ return true;
201
+ }
202
+
203
+ public get(key: string): string {
204
+ if (!this.has(key)) {
205
+ throw new ReferenceError('Key not found');
206
+ }
207
+ return fs.readFileSync(this._join(key), 'utf8');
208
+ }
209
+
210
+ public has(key: string): boolean {
211
+ return this._names.includes(key);
212
+ }
213
+
214
+ public set(key: string, value: string): this {
215
+ fs.writeFileSync(this._join(key), value);
216
+ return this;
217
+ }
218
+ }
219
+
220
+ export function gitCommitHash(repo: string = '.'): string {
221
+ repo = repo.replaceAll(/\/+/, '/').replaceAll(/\/$/, '');
222
+ const rev = fs
223
+ .readFileSync(repo + '.git/HEAD')
224
+ .toString()
225
+ .trim();
226
+ if (rev.indexOf(':') === -1) {
227
+ return rev;
228
+ } else {
229
+ return fs
230
+ .readFileSync(repo + '.git/' + rev.substring(5))
231
+ .toString()
232
+ .trim();
233
+ }
234
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './fs.js';
1
2
  export * from './list.js';
2
3
  export * from './misc.js';
3
4
  export * from './numbers.js';
package/src/objects.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type * as FS from 'fs';
2
1
  import { UnionToTuple } from './types.js';
3
2
 
4
3
  export function filterObject<O extends object, R extends object>(object: O, predicate: (key: keyof O, value: O[keyof O]) => boolean): R {
@@ -47,219 +46,6 @@ export function isJSON(str: string) {
47
46
  }
48
47
  }
49
48
 
50
- export abstract class FileMap<V> implements Map<string, V> {
51
- public get [Symbol.toStringTag](): string {
52
- return 'FileMap';
53
- }
54
-
55
- public constructor(
56
- protected readonly path: string,
57
- protected fs: typeof FS
58
- ) {
59
- if (!path) {
60
- throw new ReferenceError('No path specified');
61
- }
62
-
63
- if (!fs) {
64
- throw new ReferenceError('No filesystem API');
65
- }
66
- }
67
-
68
- protected abstract readonly _map: Map<string, V>;
69
-
70
- public abstract clear(): void;
71
-
72
- public abstract delete(key: string): boolean;
73
-
74
- public abstract get(key: string): V;
75
-
76
- public abstract has(key: string): boolean;
77
-
78
- public abstract set(key: string, value: V): this;
79
-
80
- public get size() {
81
- return this._map.size;
82
- }
83
-
84
- public get [Symbol.iterator]() {
85
- return this._map[Symbol.iterator].bind(this._map);
86
- }
87
-
88
- public get keys(): typeof this._map.keys {
89
- return this._map.keys.bind(this._map);
90
- }
91
-
92
- public get values(): typeof this._map.values {
93
- return this._map.values.bind(this._map);
94
- }
95
-
96
- public get entries(): typeof this._map.entries {
97
- return this._map.entries.bind(this._map);
98
- }
99
-
100
- public get forEach(): typeof this._map.forEach {
101
- return this._map.forEach.bind(this._map);
102
- }
103
- }
104
-
105
- export type JSONObject<Key extends string | number | symbol = string> = { [K in Key]: JSONValue };
106
-
107
- export type JSONValue<Key extends string | number | symbol = string> = string | number | boolean | JSONObject<Key> | Array<JSONValue>;
108
-
109
- export interface JSONFileMapOptions {
110
- /**
111
- * Should an invalid JSON file be overwritten
112
- */
113
- overwrite_invalid_json: boolean;
114
-
115
- /**
116
- * FS module
117
- */
118
- fs: typeof FS;
119
- }
120
-
121
- /**
122
- * A Map overlaying a JSON file
123
- */
124
- export class JSONFileMap<T extends JSONValue = JSONValue> extends FileMap<T> {
125
- public get [Symbol.toStringTag](): 'JSONFileMap' {
126
- return 'JSONFileMap';
127
- }
128
-
129
- public constructor(
130
- path: string,
131
- public readonly options: JSONFileMapOptions
132
- ) {
133
- super(path, options.fs);
134
- if (!this.fs.existsSync(path)) {
135
- this.fs.writeFileSync(path, '{}');
136
- }
137
- }
138
-
139
- public get _map(): Map<string, T> {
140
- const content = this.fs.readFileSync(this.path, 'utf8');
141
- if (!isJSON(content)) {
142
- if (!this.options.overwrite_invalid_json) {
143
- throw new SyntaxError('Invalid JSON file: ' + this.path);
144
- }
145
- console.warn('Invalid JSON file (overwriting): ' + this.path);
146
- this.clear();
147
- return new Map();
148
- }
149
- return new Map(Object.entries(JSON.parse(content)));
150
- }
151
-
152
- public _write(map: Map<string, T>) {
153
- if (!this.fs.existsSync(this.path)) {
154
- this.fs.writeFileSync(this.path, '{}');
155
- }
156
- const content = JSON.stringify(Object.fromEntries(map));
157
- this.fs.writeFileSync(this.path, content);
158
- }
159
-
160
- public clear() {
161
- this.fs.writeFileSync(this.path, '{}');
162
- }
163
-
164
- public delete(key: string): boolean {
165
- const map = this._map;
166
- const rt = map.delete(key);
167
- this._write(map);
168
- return rt;
169
- }
170
-
171
- public get<U extends T>(key: string): U {
172
- return this._map.get(key) as U;
173
- }
174
-
175
- public has(key: string): boolean {
176
- return this._map.has(key);
177
- }
178
-
179
- public set(key: string, value: T): this {
180
- const map = this._map;
181
- map.set(key, value);
182
- this._write(map);
183
- return this;
184
- }
185
- }
186
-
187
- export interface FolderMapOptions {
188
- /**
189
- * Suffix to append to keys to resolve file names
190
- */
191
- suffix: string;
192
-
193
- fs: typeof FS;
194
- }
195
-
196
- /**
197
- * A Map overlaying a folder
198
- */
199
- export class FolderMap extends FileMap<string> {
200
- public get [Symbol.toStringTag](): 'FolderMap' {
201
- return 'FolderMap';
202
- }
203
-
204
- public constructor(
205
- path: string,
206
- public readonly options: Partial<FolderMapOptions>
207
- ) {
208
- super(path, options.fs!);
209
- }
210
-
211
- protected get _names(): string[] {
212
- return this.fs
213
- .readdirSync(this.path)
214
- .filter(p => p.endsWith(this.options.suffix || ''))
215
- .map(p => p.slice(0, -this.options.suffix!.length));
216
- }
217
-
218
- protected _join(path: string): string {
219
- return `${this.path}/${path}${this.options.suffix}`;
220
- }
221
-
222
- protected get _map(): Map<string, string> {
223
- const entries: [string, string][] = [];
224
- for (const name of this._names) {
225
- const content = this.fs.readFileSync(this._join(name), 'utf8');
226
- entries.push([name, content]);
227
- }
228
- return new Map(entries);
229
- }
230
-
231
- public clear(): void {
232
- for (const name of this._names) {
233
- this.fs.unlinkSync(this._join(name));
234
- }
235
- }
236
-
237
- public delete(key: string): boolean {
238
- if (!this.has(key)) {
239
- return false;
240
- }
241
-
242
- this.fs.unlinkSync(this._join(key));
243
- return true;
244
- }
245
-
246
- public get(key: string): string {
247
- if (!this.has(key)) {
248
- throw new ReferenceError('Key not found');
249
- }
250
- return this.fs.readFileSync(this._join(key), 'utf8');
251
- }
252
-
253
- public has(key: string): boolean {
254
- return this._names.includes(key);
255
- }
256
-
257
- public set(key: string, value: string): this {
258
- this.fs.writeFileSync(this._join(key), value);
259
- return this;
260
- }
261
- }
262
-
263
49
  export function resolveConstructors(object: object): string[] {
264
50
  const constructors = [];
265
51
  let prototype = object;