utilium 0.4.4 → 0.5.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/dist/fs.d.ts +70 -0
- package/dist/fs.js +169 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/objects.d.ts +0 -72
- package/dist/objects.js +0 -146
- package/package.json +1 -1
- package/src/fs.ts +234 -0
- package/src/index.ts +1 -0
- package/src/objects.ts +0 -214
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(/\/+/g, '/').replaceAll(/\/$/g, '');
|
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
package/dist/index.js
CHANGED
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
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(/\/+/g, '/').replaceAll(/\/$/g, '');
|
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
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;
|