@brianbuie/node-kit 0.8.0 → 0.9.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 Brian Buie
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -249,10 +249,11 @@ export class File {
249
249
  get exists()
250
250
  delete()
251
251
  read()
252
- write(contents: string)
253
- async streamFrom(...options: Parameters<(typeof Readable)["from"]>)
254
- append(lines: string | string[])
255
252
  lines()
253
+ get readStream()
254
+ get writeStream()
255
+ write(contents: string | ReadableStream)
256
+ append(lines: string | string[])
256
257
  static get FileType()
257
258
  json<T>(contents?: T)
258
259
  static get json()
@@ -295,13 +296,15 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
295
296
  ---
296
297
  ## Class: FileType
297
298
 
299
+ A generic file adaptor, extended by specific file type implementations
300
+
298
301
  ```ts
299
- export class FileType<T = string> {
302
+ export class FileType {
300
303
  file;
301
- constructor(filepath: string, contents?: T)
304
+ constructor(filepath: string, contents?: string)
302
305
  get exists()
303
- delete()
304
306
  get path()
307
+ delete()
305
308
  }
306
309
  ```
307
310
 
@@ -310,10 +313,14 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
310
313
  ---
311
314
  ## Class: FileTypeCsv
312
315
 
316
+ Comma separated values (.csv).
317
+ Input rows as objects, keys are used as column headers
318
+
313
319
  ```ts
314
320
  export class FileTypeCsv<Row extends object> extends FileType {
315
321
  constructor(filepath: string)
316
322
  async write(rows: Row[], keys?: Key<Row>[])
323
+ #parseVal(val: string)
317
324
  async read()
318
325
  }
319
326
  ```
@@ -325,6 +332,9 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
325
332
  ---
326
333
  ## Class: FileTypeJson
327
334
 
335
+ A .json file that maintains data type when reading/writing.
336
+ This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
337
+
328
338
  ```ts
329
339
  export class FileTypeJson<T> extends FileType {
330
340
  constructor(filepath: string, contents?: T)
@@ -340,6 +350,8 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
340
350
  ---
341
351
  ## Class: FileTypeNdjson
342
352
 
353
+ New-line delimited json file (.ndjson)
354
+
343
355
  ```ts
344
356
  export class FileTypeNdjson<T extends object> extends FileType {
345
357
  constructor(filepath: string, lines?: T | T[])
package/package.json CHANGED
@@ -1,28 +1,27 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/brianbuie/node-kit.git"
8
8
  },
9
9
  "scripts": {
10
- "docs": "node ./node_modules/ts2md/out/src/ts2md.js --firstHeadingLevel=1",
11
- "test": "tsc && node --test \"dist/**/*.test.js\" --quiet",
12
- "preversion": "npm test && npm run docs",
10
+ "build": "tsdown && node ./node_modules/ts2md/out/src/ts2md.js --firstHeadingLevel=1",
11
+ "test": "tsc && node --test \"src/*.test.ts\" --quiet",
12
+ "preversion": "npm test && npm run build",
13
13
  "postversion": "git push --follow-tags"
14
14
  },
15
15
  "type": "module",
16
16
  "exports": {
17
17
  ".": {
18
- "types": "./dist/index.d.ts",
19
- "default": "./dist/index.js"
18
+ "default": "./dist/index.mjs",
19
+ "types": "./dist/index.d.mts"
20
20
  }
21
21
  },
22
22
  "files": [
23
23
  "src",
24
24
  "dist",
25
- "!dist/**/*.test.*",
26
25
  "README.md"
27
26
  ],
28
27
  "engines": {
@@ -41,6 +40,7 @@
41
40
  "@types/lodash-es": "^4.17.12",
42
41
  "@types/node": "^24.9.1",
43
42
  "ts2md": "^0.2.8",
43
+ "tsdown": "^0.16.6",
44
44
  "typescript": "^5.9.3"
45
45
  }
46
46
  }
package/src/Cache.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Duration, isAfter, add } from 'date-fns';
2
- import { temp } from './Dir.js';
2
+ import { temp } from './Dir.ts';
3
3
 
4
4
  const cacheDir = temp.dir('cache');
5
5
 
package/src/Dir.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { Dir, TempDir, temp } from './Dir.js';
3
+ import { Dir, TempDir, temp } from './Dir.ts';
4
4
 
5
5
  describe('Dir', () => {
6
6
  const testDir = temp.dir('dir-test');
package/src/Dir.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import sanitizeFilename from 'sanitize-filename';
4
- import { File } from './File.js';
4
+ import { File } from './File.ts';
5
5
 
6
6
  /**
7
7
  * Reference to a specific directory with helpful methods for resolving filepaths,
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { Fetcher } from './Fetcher.js';
3
+ import { Fetcher } from './Fetcher.ts';
4
4
 
5
5
  describe('Fetcher', () => {
6
6
  const statusApi = new Fetcher({ base: 'https://mock.httpstatus.io' });
package/src/File.test.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { temp } from './Dir.js';
4
- import { File } from './File.js';
3
+ import { temp } from './Dir.ts';
4
+ import { File } from './File.ts';
5
5
 
6
6
  const testDir = temp.dir('file-test');
7
7
  testDir.clear();
@@ -14,6 +14,17 @@ const thing = {
14
14
  e: null,
15
15
  };
16
16
 
17
+ describe('File', () => {
18
+ it('Handles request body as stream input', async () => {
19
+ const img = testDir.file('image.jpg');
20
+ await fetch('https://testingbot.com/free-online-tools/random-avatar/300').then((res) => {
21
+ if (!res.body) throw new Error('No response body');
22
+ return img.write(res.body);
23
+ });
24
+ assert(img.exists);
25
+ });
26
+ });
27
+
17
28
  describe('FileType', () => {
18
29
  it('Creates instances', () => {
19
30
  const test1 = new File.FileType(testDir.filepath('test1.txt'));
@@ -35,20 +46,23 @@ describe('FileType', () => {
35
46
  });
36
47
  });
37
48
 
38
- describe('File', () => {
39
- it('Handles request body as stream input', async () => {
40
- const res = await fetch('https://testingbot.com/free-online-tools/random-avatar/300');
41
- const img = testDir.file('image.jpg');
42
- if (res.body) {
43
- await img.streamFrom(res.body);
44
- assert(img.exists);
45
- } else {
46
- assert(false, 'No response body');
47
- }
49
+ describe('FileTypeJson', () => {
50
+ it('Saves data as json', () => {
51
+ const file = testDir.file('jsonfile-data').json(thing);
52
+ assert.deepStrictEqual(file.read(), thing);
53
+ file.write(thing);
54
+ assert.deepStrictEqual(file.read(), thing);
55
+ });
56
+
57
+ it('Does not create file when reading', () => {
58
+ const file = testDir.file('test123').json();
59
+ const contents = file.read();
60
+ assert(contents === undefined);
61
+ assert(!file.exists);
48
62
  });
49
63
  });
50
64
 
51
- describe('File.ndjson', () => {
65
+ describe('FileTypeNdjson', () => {
52
66
  it('Appends new lines correctly', () => {
53
67
  const file = testDir.file('appends-lines').ndjson();
54
68
  file.delete();
@@ -70,23 +84,7 @@ describe('File.ndjson', () => {
70
84
  });
71
85
  });
72
86
 
73
- describe('File.json', () => {
74
- it('Saves data as json', () => {
75
- const file = testDir.file('jsonfile-data').json(thing);
76
- assert.deepStrictEqual(file.read(), thing);
77
- file.write(thing);
78
- assert.deepStrictEqual(file.read(), thing);
79
- });
80
-
81
- it('Does not create file when reading', () => {
82
- const file = testDir.file('test123').json();
83
- const contents = file.read();
84
- assert(contents === undefined);
85
- assert(!file.exists);
86
- });
87
- });
88
-
89
- describe('File.csv', () => {
87
+ describe('FileTypeCsv', () => {
90
88
  it('Saves data as csv', async () => {
91
89
  const things = [thing, thing, thing];
92
90
  const file = await testDir.file('csv-data').csv(things);
@@ -95,4 +93,10 @@ describe('File.csv', () => {
95
93
  assert.deepEqual(row, thing);
96
94
  });
97
95
  });
96
+ it('Reads file that does not exist', async () => {
97
+ const file = await testDir.file('bogus').csv();
98
+ const contents = await file.read();
99
+ assert(Array.isArray(contents));
100
+ assert(contents.length === 0);
101
+ });
98
102
  });
package/src/File.ts CHANGED
@@ -2,8 +2,8 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { Readable } from 'node:stream';
4
4
  import { finished } from 'node:stream/promises';
5
- import { writeToString, parseString } from 'fast-csv';
6
- import { snapshot } from './snapshot.js';
5
+ import { writeToStream, parseStream } from 'fast-csv';
6
+ import { snapshot } from './snapshot.ts';
7
7
 
8
8
  /**
9
9
  * WARNING: API will change!
@@ -27,13 +27,28 @@ export class File {
27
27
  return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
28
28
  }
29
29
 
30
- write(contents: string) {
30
+ /**
31
+ * @returns lines as strings, removes trailing '\n'
32
+ */
33
+ lines() {
34
+ const contents = (this.read() || '').split('\n');
35
+ return contents.slice(0, contents.length - 1);
36
+ }
37
+
38
+ get readStream() {
39
+ return this.exists ? fs.createReadStream(this.path) : Readable.from([]);
40
+ }
41
+
42
+ get writeStream() {
31
43
  fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
32
- fs.writeFileSync(this.path, contents);
44
+ return fs.createWriteStream(this.path);
33
45
  }
34
46
 
35
- async streamFrom(...options: Parameters<(typeof Readable)['from']>) {
36
- return finished(Readable.from(...options).pipe(fs.createWriteStream(this.path)));
47
+ write(contents: string | ReadableStream) {
48
+ fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
49
+ if (typeof contents === 'string') return fs.writeFileSync(this.path, contents);
50
+ if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));
51
+ throw new Error(`Invalid content type: ${typeof contents}`);
37
52
  }
38
53
 
39
54
  /**
@@ -46,14 +61,6 @@ export class File {
46
61
  fs.appendFileSync(this.path, contents + '\n');
47
62
  }
48
63
 
49
- /**
50
- * @returns lines as strings, removes trailing '\n'
51
- */
52
- lines() {
53
- const contents = (this.read() || '').split('\n');
54
- return contents.slice(0, contents.length - 1);
55
- }
56
-
57
64
  static get FileType() {
58
65
  return FileType;
59
66
  }
@@ -85,32 +92,34 @@ export class File {
85
92
  }
86
93
  }
87
94
 
88
- export class FileType<T = string> {
95
+ /**
96
+ * A generic file adaptor, extended by specific file type implementations
97
+ */
98
+ export class FileType {
89
99
  file;
90
100
 
91
- constructor(filepath: string, contents?: T) {
101
+ constructor(filepath: string, contents?: string) {
92
102
  this.file = new File(filepath);
93
- if (contents) {
94
- if (typeof contents !== 'string') {
95
- throw new Error('File contents must be a string');
96
- }
97
- this.file.write(contents);
98
- }
103
+ if (contents) this.file.write(contents);
99
104
  }
100
105
 
101
106
  get exists() {
102
107
  return this.file.exists;
103
108
  }
104
109
 
105
- delete() {
106
- this.file.delete();
107
- }
108
-
109
110
  get path() {
110
111
  return this.file.path;
111
112
  }
113
+
114
+ delete() {
115
+ this.file.delete();
116
+ }
112
117
  }
113
118
 
119
+ /**
120
+ * A .json file that maintains data type when reading/writing.
121
+ * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
122
+ */
114
123
  export class FileTypeJson<T> extends FileType {
115
124
  constructor(filepath: string, contents?: T) {
116
125
  super(filepath.endsWith('.json') ? filepath : filepath + '.json');
@@ -127,6 +136,10 @@ export class FileTypeJson<T> extends FileType {
127
136
  }
128
137
  }
129
138
 
139
+ /**
140
+ * New-line delimited json file (.ndjson)
141
+ * @see https://jsonltools.com/ndjson-format-specification
142
+ */
130
143
  export class FileTypeNdjson<T extends object> extends FileType {
131
144
  constructor(filepath: string, lines?: T | T[]) {
132
145
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
@@ -146,6 +159,10 @@ export class FileTypeNdjson<T extends object> extends FileType {
146
159
 
147
160
  type Key<T extends object> = keyof T;
148
161
 
162
+ /**
163
+ * Comma separated values (.csv).
164
+ * Input rows as objects, keys are used as column headers
165
+ */
149
166
  export class FileTypeCsv<Row extends object> extends FileType {
150
167
  constructor(filepath: string) {
151
168
  super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
@@ -162,36 +179,34 @@ export class FileTypeCsv<Row extends object> extends FileType {
162
179
  }
163
180
  const headers = Array.from(headerSet);
164
181
  const outRows = rows.map((row) => headers.map((key) => row[key]));
165
- const contents = await writeToString([headers, ...outRows]);
166
- this.file.write(contents);
182
+ return finished(writeToStream(this.file.writeStream, [headers, ...outRows]));
183
+ }
184
+
185
+ #parseVal(val: string) {
186
+ if (val.toLowerCase() === 'false') return false;
187
+ if (val.toLowerCase() === 'true') return true;
188
+ if (val.length === 0) return null;
189
+ if (/^[\.0-9]+$/.test(val)) return Number(val);
190
+ return val;
167
191
  }
168
192
 
169
193
  async read() {
170
194
  return new Promise<Row[]>((resolve, reject) => {
171
195
  const parsed: Row[] = [];
172
- const content = this.file.read();
173
- if (!content) return resolve(parsed);
174
- function parseVal(val: string) {
175
- if (val.toLowerCase() === 'false') return false;
176
- if (val.toLowerCase() === 'true') return true;
177
- if (val.length === 0) return null;
178
- if (/^[\.0-9]+$/.test(val)) return Number(val);
179
- return val;
180
- }
181
- parseString(content, { headers: true })
196
+ parseStream(this.file.readStream, { headers: true })
182
197
  .on('error', (e) => reject(e))
183
- .on('end', () => resolve(parsed))
184
198
  .on('data', (raw: Record<Key<Row>, string>) => {
185
199
  parsed.push(
186
200
  Object.entries(raw).reduce(
187
201
  (all, [key, val]) => ({
188
202
  ...all,
189
- [key]: parseVal(val as string),
203
+ [key]: this.#parseVal(val as string),
190
204
  }),
191
205
  {} as Row,
192
206
  ),
193
207
  );
194
- });
208
+ })
209
+ .on('end', () => resolve(parsed));
195
210
  });
196
211
  }
197
212
  }
package/src/Log.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { Log } from './Log.js';
3
+ import { Log } from './Log.ts';
4
4
 
5
5
  describe('Log', () => {
6
6
  it('Throws error', () => {
package/src/Log.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { inspect } from 'node:util';
2
2
  import { isObjectLike } from 'lodash-es';
3
3
  import chalk, { type ChalkInstance } from 'chalk';
4
- import { snapshot } from './snapshot.js';
4
+ import { snapshot } from './snapshot.ts';
5
5
 
6
6
  type Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';
7
7
 
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { TypeWriter } from './TypeWriter.js';
3
+ import { TypeWriter } from './TypeWriter.ts';
4
4
 
5
5
  describe('TypeWriter', () => {
6
6
  const test = new TypeWriter('Test');
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { Dir, TempDir, temp } from './Dir.js';
2
- export { Cache } from './Cache.js';
3
- export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.js';
4
- export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
5
- export { Log } from './Log.js';
6
- export { snapshot } from './snapshot.js';
7
- export { timeout } from './timeout.js';
8
- export { TypeWriter } from './TypeWriter.js';
1
+ export { Dir, TempDir, temp } from './Dir.ts';
2
+ export { Cache } from './Cache.ts';
3
+ export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.ts';
4
+ export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.ts';
5
+ export { Log } from './Log.ts';
6
+ export { snapshot } from './snapshot.ts';
7
+ export { timeout } from './timeout.ts';
8
+ export { TypeWriter } from './TypeWriter.ts';
@@ -1,7 +1,7 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { snapshot } from './snapshot.js';
4
- import { temp } from './Dir.js';
3
+ import { snapshot } from './snapshot.ts';
4
+ import { temp } from './Dir.ts';
5
5
 
6
6
  describe('snapshot', () => {
7
7
  it('Captures Error details', () => {
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { timeout } from './timeout.js';
3
+ import { timeout } from './timeout.ts';
4
4
 
5
5
  describe('timeout', () => {
6
6
  it('Waits correct amount of time', async () => {
package/dist/Cache.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import { type Duration } from 'date-fns';
2
- /**
3
- * Save data to a local file with an expiration.
4
- * Fresh/stale data is returned with a flag for if it's fresh or not,
5
- * so stale data can still be used if needed.
6
- */
7
- export declare class Cache<T> {
8
- file: import("./File.js").FileTypeJson<{
9
- savedAt: string;
10
- data: T;
11
- }>;
12
- ttl: Duration;
13
- constructor(key: string, ttl: number | Duration, initialData?: T);
14
- write(data: T): void;
15
- read(): [T | undefined, boolean];
16
- }
package/dist/Cache.js DELETED
@@ -1,26 +0,0 @@
1
- import { isAfter, add } from 'date-fns';
2
- import { temp } from './Dir.js';
3
- const cacheDir = temp.dir('cache');
4
- /**
5
- * Save data to a local file with an expiration.
6
- * Fresh/stale data is returned with a flag for if it's fresh or not,
7
- * so stale data can still be used if needed.
8
- */
9
- export class Cache {
10
- file;
11
- ttl;
12
- constructor(key, ttl, initialData) {
13
- this.file = cacheDir.file(key).json();
14
- this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;
15
- if (initialData)
16
- this.write(initialData);
17
- }
18
- write(data) {
19
- this.file.write({ savedAt: new Date().toUTCString(), data });
20
- }
21
- read() {
22
- const { savedAt, data } = this.file.read() || {};
23
- const isFresh = Boolean(savedAt && isAfter(add(savedAt, this.ttl), new Date()));
24
- return [data, isFresh];
25
- }
26
- }
package/dist/Dir.d.ts DELETED
@@ -1,44 +0,0 @@
1
- import { File } from './File.js';
2
- /**
3
- * Reference to a specific directory with helpful methods for resolving filepaths,
4
- * sanitizing filenames, and saving files.
5
- */
6
- export declare class Dir {
7
- path: string;
8
- /**
9
- * @param path can be relative to workspace or absolute
10
- */
11
- constructor(_path: string);
12
- create(): void;
13
- /**
14
- * Create a new Dir inside the current Dir
15
- * @param subPath to create in current Dir
16
- * @example
17
- * const folder = new Dir('example');
18
- * // folder.path = './example'
19
- * const child = folder.subDir('path/to/dir');
20
- * // child.path = './example/path/to/dir'
21
- */
22
- dir(subPath: string): Dir;
23
- sanitize(name: string): string;
24
- /**
25
- * @param base - The file name with extension
26
- * @example
27
- * const folder = new Dir('example');
28
- * const filepath = folder.resolve('file.json');
29
- * // 'example/file.json'
30
- */
31
- filepath(base: string): string;
32
- file(base: string): File;
33
- }
34
- /**
35
- * Extends Dir class with method to `clear()` contents
36
- */
37
- export declare class TempDir extends Dir {
38
- dir(subPath: string): TempDir;
39
- clear(): void;
40
- }
41
- /**
42
- * Common temp dir location
43
- */
44
- export declare const temp: TempDir;
package/dist/Dir.js DELETED
@@ -1,64 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import sanitizeFilename from 'sanitize-filename';
4
- import { File } from './File.js';
5
- /**
6
- * Reference to a specific directory with helpful methods for resolving filepaths,
7
- * sanitizing filenames, and saving files.
8
- */
9
- export class Dir {
10
- path;
11
- /**
12
- * @param path can be relative to workspace or absolute
13
- */
14
- constructor(_path) {
15
- this.path = _path;
16
- }
17
- create() {
18
- fs.mkdirSync(this.path, { recursive: true });
19
- }
20
- /**
21
- * Create a new Dir inside the current Dir
22
- * @param subPath to create in current Dir
23
- * @example
24
- * const folder = new Dir('example');
25
- * // folder.path = './example'
26
- * const child = folder.subDir('path/to/dir');
27
- * // child.path = './example/path/to/dir'
28
- */
29
- dir(subPath) {
30
- return new Dir(path.resolve(this.path, subPath));
31
- }
32
- sanitize(name) {
33
- return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' }).slice(-200);
34
- }
35
- /**
36
- * @param base - The file name with extension
37
- * @example
38
- * const folder = new Dir('example');
39
- * const filepath = folder.resolve('file.json');
40
- * // 'example/file.json'
41
- */
42
- filepath(base) {
43
- return path.resolve(this.path, this.sanitize(base));
44
- }
45
- file(base) {
46
- return new File(this.filepath(base));
47
- }
48
- }
49
- /**
50
- * Extends Dir class with method to `clear()` contents
51
- */
52
- export class TempDir extends Dir {
53
- dir(subPath) {
54
- return new TempDir(path.resolve(this.path, subPath));
55
- }
56
- clear() {
57
- fs.rmSync(this.path, { recursive: true, force: true });
58
- this.create();
59
- }
60
- }
61
- /**
62
- * Common temp dir location
63
- */
64
- export const temp = new TempDir('.temp');