@gesslar/toolkit 0.0.12 → 0.1.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/package.json +6 -15
- package/src/index.js +1 -1
- package/src/lib/Cache.js +3 -4
- package/src/lib/Data.js +31 -24
- package/src/lib/DirectoryObject.js +77 -7
- package/src/lib/FS.js +191 -0
- package/src/lib/FileObject.js +200 -11
- package/src/lib/Glog.js +15 -7
- package/src/lib/Type.js +20 -7
- package/src/lib/Util.js +11 -1
- package/src/types/Cache.d.ts +1 -1
- package/src/types/DirectoryObject.d.ts +18 -2
- package/src/types/FS.d.ts +31 -0
- package/src/types/FileObject.d.ts +55 -19
- package/src/types/index.d.ts +2 -2
- package/src/lib/File.js +0 -414
- package/src/types/File.d.ts +0 -83
package/src/lib/FileObject.js
CHANGED
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* resolution and existence checks.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import JSON5 from "json5"
|
|
8
|
+
import fs from "node:fs/promises"
|
|
7
9
|
import path from "node:path"
|
|
8
10
|
import util from "node:util"
|
|
11
|
+
import YAML from "yaml"
|
|
9
12
|
|
|
10
13
|
import DirectoryObject from "./DirectoryObject.js"
|
|
11
|
-
import
|
|
14
|
+
import FS from "./FS.js"
|
|
15
|
+
import Sass from "./Sass.js"
|
|
16
|
+
import Valid from "./Valid.js"
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* FileObject encapsulates metadata and operations for a file, including path
|
|
@@ -26,7 +31,20 @@ import File from "./File.js"
|
|
|
26
31
|
* @property {Promise<boolean>} exists - Whether the file exists (async)
|
|
27
32
|
*/
|
|
28
33
|
|
|
29
|
-
export default class FileObject {
|
|
34
|
+
export default class FileObject extends FS {
|
|
35
|
+
/**
|
|
36
|
+
* Configuration mapping data types to their respective parser modules for loadData method.
|
|
37
|
+
* Each parser module must have a .parse() method that accepts a string and returns parsed data.
|
|
38
|
+
*
|
|
39
|
+
* @type {{[key: string]: Array<typeof JSON5 | typeof YAML>}}
|
|
40
|
+
*/
|
|
41
|
+
static dataLoaderConfig = Object.freeze({
|
|
42
|
+
json5: [JSON5],
|
|
43
|
+
json: [JSON5],
|
|
44
|
+
yaml: [YAML],
|
|
45
|
+
any: [JSON5, YAML]
|
|
46
|
+
})
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* @type {object}
|
|
32
50
|
* @private
|
|
@@ -59,19 +77,26 @@ export default class FileObject {
|
|
|
59
77
|
* @param {DirectoryObject|string|null} [directory] - The parent directory (object or string)
|
|
60
78
|
*/
|
|
61
79
|
constructor(fileName, directory=null) {
|
|
62
|
-
|
|
80
|
+
super()
|
|
63
81
|
|
|
64
|
-
|
|
82
|
+
if(!fileName || typeof fileName !== "string" || fileName.length === 0) {
|
|
83
|
+
throw Sass.new("fileName must be a non-empty string")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const fixedFile = FS.fixSlashes(fileName)
|
|
87
|
+
|
|
88
|
+
const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
|
|
65
89
|
|
|
66
|
-
if(!directory)
|
|
90
|
+
if(!directory) {
|
|
67
91
|
directory = new DirectoryObject(dir)
|
|
92
|
+
} else if(typeof directory === "string") {
|
|
93
|
+
directory = new DirectoryObject(directory)
|
|
94
|
+
}
|
|
68
95
|
|
|
69
|
-
const final = path.
|
|
70
|
-
? fixedFile
|
|
71
|
-
: path.resolve(directory?.path ?? ".", fixedFile)
|
|
96
|
+
const final = FS.resolvePath(directory?.path ?? ".", fixedFile)
|
|
72
97
|
|
|
73
98
|
const resolved = final
|
|
74
|
-
const fileUri =
|
|
99
|
+
const fileUri = FS.pathToUri(resolved)
|
|
75
100
|
|
|
76
101
|
this.#meta.supplied = fixedFile
|
|
77
102
|
this.#meta.path = resolved
|
|
@@ -80,7 +105,7 @@ export default class FileObject {
|
|
|
80
105
|
this.#meta.extension = ext
|
|
81
106
|
this.#meta.module = path.basename(this.supplied, this.extension)
|
|
82
107
|
|
|
83
|
-
const {dir: newDir} =
|
|
108
|
+
const {dir: newDir} = this.#deconstructFilenameToParts(this.path)
|
|
84
109
|
|
|
85
110
|
this.#meta.directory = new DirectoryObject(newDir)
|
|
86
111
|
|
|
@@ -92,6 +117,9 @@ export default class FileObject {
|
|
|
92
117
|
*
|
|
93
118
|
* @returns {string} string representation of the FileObject
|
|
94
119
|
*/
|
|
120
|
+
toString() {
|
|
121
|
+
return `[FileObject: ${this.path}]`
|
|
122
|
+
}
|
|
95
123
|
|
|
96
124
|
/**
|
|
97
125
|
* Returns a JSON representation of the FileObject.
|
|
@@ -127,7 +155,7 @@ export default class FileObject {
|
|
|
127
155
|
* @returns {Promise<boolean>} - A Promise that resolves to true or false
|
|
128
156
|
*/
|
|
129
157
|
get exists() {
|
|
130
|
-
return
|
|
158
|
+
return this.#fileExists()
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
/**
|
|
@@ -219,4 +247,165 @@ export default class FileObject {
|
|
|
219
247
|
get directory() {
|
|
220
248
|
return this.#meta.directory
|
|
221
249
|
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check if a file can be read. Returns true if the file can be read, false
|
|
253
|
+
*
|
|
254
|
+
* @returns {Promise<boolean>} Whether the file can be read
|
|
255
|
+
*/
|
|
256
|
+
async canRead() {
|
|
257
|
+
try {
|
|
258
|
+
await fs.access(this.path, fs.constants.R_OK)
|
|
259
|
+
|
|
260
|
+
return true
|
|
261
|
+
} catch(_) {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Check if a file can be written. Returns true if the file can be written,
|
|
268
|
+
*
|
|
269
|
+
* @returns {Promise<boolean>} Whether the file can be written
|
|
270
|
+
*/
|
|
271
|
+
async canWrite() {
|
|
272
|
+
try {
|
|
273
|
+
await fs.access(this.path, fs.constants.W_OK)
|
|
274
|
+
|
|
275
|
+
return true
|
|
276
|
+
} catch(_) {
|
|
277
|
+
return false
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check if a file exists
|
|
283
|
+
*
|
|
284
|
+
* @returns {Promise<boolean>} Whether the file exists
|
|
285
|
+
*/
|
|
286
|
+
async #fileExists() {
|
|
287
|
+
try {
|
|
288
|
+
await fs.access(this.path, fs.constants.F_OK)
|
|
289
|
+
|
|
290
|
+
return true
|
|
291
|
+
} catch(_) {
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Determines the size of a file.
|
|
298
|
+
*
|
|
299
|
+
* @returns {Promise<number?>} - The size of the file or null, if it doesn't exist.
|
|
300
|
+
*/
|
|
301
|
+
async size() {
|
|
302
|
+
try {
|
|
303
|
+
const stat = await fs.stat(this.path)
|
|
304
|
+
|
|
305
|
+
return stat.size
|
|
306
|
+
} catch(_) {
|
|
307
|
+
return null
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Gets the last modification time of a file.
|
|
313
|
+
* Used by the caching system to determine if cached data is still valid.
|
|
314
|
+
*
|
|
315
|
+
* @returns {Promise<Date?>} The last modification time, or null if file doesn't exist
|
|
316
|
+
*/
|
|
317
|
+
async modified() {
|
|
318
|
+
try {
|
|
319
|
+
const stat = await fs.stat(this.path)
|
|
320
|
+
|
|
321
|
+
return stat.mtime
|
|
322
|
+
} catch(_) {
|
|
323
|
+
return null
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* @typedef {object} FileParts
|
|
329
|
+
* @property {string} base - The file name with extension
|
|
330
|
+
* @property {string} dir - The directory path
|
|
331
|
+
* @property {string} ext - The file extension (including dot)
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Deconstruct a filename into parts
|
|
336
|
+
*
|
|
337
|
+
* @param {string} fileName - The filename to deconstruct
|
|
338
|
+
* @returns {FileParts} The filename parts
|
|
339
|
+
*/
|
|
340
|
+
#deconstructFilenameToParts(fileName) {
|
|
341
|
+
Valid.assert(typeof fileName === "string" && fileName.length > 0,
|
|
342
|
+
"file must be a non-zero length string", 1)
|
|
343
|
+
|
|
344
|
+
return path.parse(fileName)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Reads the content of a file asynchronously.
|
|
349
|
+
*
|
|
350
|
+
* @param {string} [encoding] - The encoding to read the file as.
|
|
351
|
+
* @returns {Promise<string>} The file contents
|
|
352
|
+
*/
|
|
353
|
+
async read(encoding="utf8") {
|
|
354
|
+
const filePath = this.path
|
|
355
|
+
|
|
356
|
+
if(!(await this.exists))
|
|
357
|
+
throw Sass.new(`No such file '${filePath}'`)
|
|
358
|
+
|
|
359
|
+
if(!filePath)
|
|
360
|
+
throw Sass.new("No absolute path in file map")
|
|
361
|
+
|
|
362
|
+
return await fs.readFile(filePath, encoding)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Writes content to a file synchronously.
|
|
367
|
+
*
|
|
368
|
+
* @param {string} content - The content to write
|
|
369
|
+
* @param {string} encoding - The encoding in which to write.
|
|
370
|
+
* @returns {Promise<void>}
|
|
371
|
+
*/
|
|
372
|
+
async write(content, encoding="utf8") {
|
|
373
|
+
if(!this.path)
|
|
374
|
+
throw Sass.new("No absolute path in file")
|
|
375
|
+
|
|
376
|
+
await fs.writeFile(this.path, content, encoding)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Loads an object from JSON or YAML provided a fileMap
|
|
381
|
+
*
|
|
382
|
+
* @param {string} [type] - The expected type of data to parse.
|
|
383
|
+
* @param {string} [encoding] - The encoding to read the file as.
|
|
384
|
+
* @returns {Promise<unknown>} The parsed data object.
|
|
385
|
+
*/
|
|
386
|
+
async loadData(type="any", encoding="utf8") {
|
|
387
|
+
const content = await this.read(encoding)
|
|
388
|
+
const toTry = {
|
|
389
|
+
json5: [JSON5],
|
|
390
|
+
json: [JSON5],
|
|
391
|
+
yaml: [YAML],
|
|
392
|
+
any: [JSON5,YAML]
|
|
393
|
+
}[type.toLowerCase()]
|
|
394
|
+
|
|
395
|
+
if(!toTry) {
|
|
396
|
+
throw Sass.new(`Unsupported data type '${type}'. Supported types: json, json5, yaml, any`)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
for(const format of toTry) {
|
|
400
|
+
try {
|
|
401
|
+
const result = format.parse(content)
|
|
402
|
+
|
|
403
|
+
return result
|
|
404
|
+
} catch {
|
|
405
|
+
// nothing to see here
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
throw Sass.new(`Content is neither valid JSON5 nor valid YAML:\n'${this.path}'`)
|
|
410
|
+
}
|
|
222
411
|
}
|
package/src/lib/Glog.js
CHANGED
|
@@ -21,9 +21,9 @@ import console from "node:console"
|
|
|
21
21
|
*/
|
|
22
22
|
class Glog {
|
|
23
23
|
/** @type {number} Current log level threshold (0-5) */
|
|
24
|
-
logLevel = 0
|
|
24
|
+
static logLevel = 0
|
|
25
25
|
/** @type {string} Prefix to prepend to all log messages */
|
|
26
|
-
logPrefix = ""
|
|
26
|
+
static logPrefix = ""
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Sets the log prefix for all subsequent log messages.
|
|
@@ -39,7 +39,7 @@ class Glog {
|
|
|
39
39
|
static setLogPrefix(prefix) {
|
|
40
40
|
this.logPrefix = prefix
|
|
41
41
|
|
|
42
|
-
return
|
|
42
|
+
return this
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
@@ -57,7 +57,7 @@ class Glog {
|
|
|
57
57
|
static setLogLevel(level) {
|
|
58
58
|
this.logLevel = Data.clamp(level, 0, 5)
|
|
59
59
|
|
|
60
|
-
return
|
|
60
|
+
return this
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
@@ -75,11 +75,11 @@ class Glog {
|
|
|
75
75
|
let level, rest
|
|
76
76
|
|
|
77
77
|
if(args.length === 0) {
|
|
78
|
-
;[level=0, rest=[""]] =
|
|
78
|
+
;[level=0, rest=[""]] = []
|
|
79
79
|
} else if(args.length === 1) {
|
|
80
80
|
;[rest, level=0] = [args, 0]
|
|
81
81
|
} else {
|
|
82
|
-
;[level, ...rest] = args
|
|
82
|
+
;[level, ...rest] = typeof args[0] === "number" ? args : [0, ...args]
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
if(level > this.logLevel)
|
|
@@ -123,10 +123,18 @@ class Glog {
|
|
|
123
123
|
// Wrap the class in a proxy
|
|
124
124
|
export default new Proxy(Glog, {
|
|
125
125
|
apply(target, thisArg, argumentsList) {
|
|
126
|
-
// When called as function:
|
|
126
|
+
// When called as function: call execute method internally
|
|
127
127
|
return target.execute(...argumentsList)
|
|
128
128
|
},
|
|
129
129
|
construct(target, argumentsList) {
|
|
130
130
|
return new target(...argumentsList)
|
|
131
|
+
},
|
|
132
|
+
get(target, prop) {
|
|
133
|
+
// Hide execute method from public API
|
|
134
|
+
if(prop === "execute") {
|
|
135
|
+
return undefined
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return target[prop]
|
|
131
139
|
}
|
|
132
140
|
})
|
package/src/lib/Type.js
CHANGED
|
@@ -156,20 +156,33 @@ export default class TypeSpec {
|
|
|
156
156
|
const matchingTypeSpec = this.filter(spec => {
|
|
157
157
|
const {typeName: allowedType, array: allowedArray} = spec
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
// Handle non-array values
|
|
160
|
+
if(!isArray && !allowedArray) {
|
|
161
|
+
if(valueType === allowedType)
|
|
162
|
+
return allowEmpty || !empty
|
|
161
163
|
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Handle array values
|
|
162
168
|
if(isArray) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
// Special case for generic "array" type
|
|
170
|
+
if(allowedType === "array" && !allowedArray)
|
|
171
|
+
return allowEmpty || !empty
|
|
172
|
+
|
|
173
|
+
// Must be an array type specification
|
|
174
|
+
if(!allowedArray)
|
|
175
|
+
return false
|
|
166
176
|
|
|
177
|
+
// Handle empty arrays
|
|
167
178
|
if(empty)
|
|
168
|
-
|
|
169
|
-
return true
|
|
179
|
+
return allowEmpty
|
|
170
180
|
|
|
181
|
+
// Check if array elements match the required type
|
|
171
182
|
return Data.isArrayUniform(value, allowedType)
|
|
172
183
|
}
|
|
184
|
+
|
|
185
|
+
return false
|
|
173
186
|
})
|
|
174
187
|
|
|
175
188
|
return matchingTypeSpec.length > 0
|
package/src/lib/Util.js
CHANGED
|
@@ -98,7 +98,7 @@ export default class Util {
|
|
|
98
98
|
.reduce((acc, curr) => acc.sign === "--" ? acc : curr, {})
|
|
99
99
|
?.option
|
|
100
100
|
})
|
|
101
|
-
.filter(
|
|
101
|
+
.filter(option => option && /^[a-zA-Z0-9]/.test(option)) // Filter out options that don't start with alphanumeric
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -184,6 +184,11 @@ export default class Util {
|
|
|
184
184
|
} catch(error) {
|
|
185
185
|
const argsDesc = args.length > 0 ? `with arguments: ${args.map(String).join(", ")}` : "with no arguments"
|
|
186
186
|
|
|
187
|
+
// If it's already a Sass error, just re-throw to avoid double-wrapping
|
|
188
|
+
if(error instanceof Sass) {
|
|
189
|
+
throw error
|
|
190
|
+
}
|
|
191
|
+
|
|
187
192
|
throw Sass.new(
|
|
188
193
|
`Processing '${event}' event ${argsDesc}.`,
|
|
189
194
|
error
|
|
@@ -214,6 +219,11 @@ export default class Util {
|
|
|
214
219
|
} catch(error) {
|
|
215
220
|
const argsDesc = args.length > 0 ? `with arguments: ${args.map(String).join(", ")}` : "with no arguments"
|
|
216
221
|
|
|
222
|
+
// If it's already a Sass error, just re-throw to avoid double-wrapping
|
|
223
|
+
if(error instanceof Sass) {
|
|
224
|
+
throw error
|
|
225
|
+
}
|
|
226
|
+
|
|
217
227
|
throw Sass.new(
|
|
218
228
|
`Processing '${event}' event ${argsDesc}.`,
|
|
219
229
|
error
|
package/src/types/Cache.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ declare class Cache {
|
|
|
24
24
|
* @returns The parsed file data (JSON5 or YAML)
|
|
25
25
|
* @throws If the file cannot be found or accessed
|
|
26
26
|
*/
|
|
27
|
-
loadCachedData(fileObject: FileObject): Promise<
|
|
27
|
+
loadCachedData(fileObject: FileObject): Promise<unknown>
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export default Cache
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
// Implementation: ../lib/DirectoryObject.js
|
|
2
2
|
// Type definitions for DirectoryObject
|
|
3
3
|
|
|
4
|
+
import FS from './FS.js'
|
|
5
|
+
import FileObject from './FileObject.js'
|
|
6
|
+
|
|
7
|
+
export interface DirectoryListing {
|
|
8
|
+
/** Array of FileObject instances */
|
|
9
|
+
files: Array<FileObject>
|
|
10
|
+
/** Array of DirectoryObject instances */
|
|
11
|
+
directories: Array<DirectoryObject>
|
|
12
|
+
}
|
|
13
|
+
|
|
4
14
|
/**
|
|
5
15
|
* DirectoryObject encapsulates metadata and operations for a directory,
|
|
6
16
|
* including path resolution and existence checks.
|
|
7
17
|
*/
|
|
8
|
-
export default class DirectoryObject {
|
|
18
|
+
export default class DirectoryObject extends FS {
|
|
9
19
|
/**
|
|
10
20
|
* Create a new DirectoryObject instance.
|
|
11
21
|
* @param directory - The directory path
|
|
@@ -53,4 +63,10 @@ export default class DirectoryObject {
|
|
|
53
63
|
isFile: boolean
|
|
54
64
|
isDirectory: boolean
|
|
55
65
|
}
|
|
56
|
-
|
|
66
|
+
|
|
67
|
+
/** List the contents of this directory */
|
|
68
|
+
read(): Promise<DirectoryListing>
|
|
69
|
+
|
|
70
|
+
/** Ensure this directory exists, creating it if necessary */
|
|
71
|
+
assureExists(options?: any): Promise<void>
|
|
72
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Implementation: ../lib/FS.js
|
|
2
|
+
// Type definitions for FS utilities
|
|
3
|
+
|
|
4
|
+
import FileObject from './FileObject.js'
|
|
5
|
+
import DirectoryObject from './DirectoryObject.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base filesystem utilities class. FileObject and DirectoryObject extend this class.
|
|
9
|
+
*/
|
|
10
|
+
export default class FS {
|
|
11
|
+
/** Fix slashes in a path */
|
|
12
|
+
static fixSlashes(pathName: string): string
|
|
13
|
+
|
|
14
|
+
/** Convert a path to a URI */
|
|
15
|
+
static pathToUri(pathName: string): string
|
|
16
|
+
|
|
17
|
+
/** Convert a URI to a path */
|
|
18
|
+
static uriToPath(pathName: string): string
|
|
19
|
+
|
|
20
|
+
/** Retrieve files matching glob pattern(s) */
|
|
21
|
+
static getFiles(glob: string | Array<string>): Promise<Array<FileObject>>
|
|
22
|
+
|
|
23
|
+
/** Compute relative path between two file system objects */
|
|
24
|
+
static relativeOrAbsolutePath(from: FileObject | DirectoryObject, to: FileObject | DirectoryObject): string
|
|
25
|
+
|
|
26
|
+
/** Merge two paths by finding overlapping segments and combining them efficiently */
|
|
27
|
+
static mergeOverlappingPaths(path1: string, path2: string, sep?: string): string
|
|
28
|
+
|
|
29
|
+
/** Resolve a path relative to another path using various strategies. Handles absolute paths, relative navigation, and overlap-based merging */
|
|
30
|
+
static resolvePath(fromPath: string, toPath: string): string
|
|
31
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// Implementation: ../lib/FileObject.js
|
|
2
2
|
|
|
3
3
|
import DirectoryObject from './DirectoryObject.js'
|
|
4
|
+
import FS from './FS.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for data loading parsers in loadData method.
|
|
8
|
+
* Maps supported data types to their respective parser functions.
|
|
9
|
+
*/
|
|
10
|
+
export interface DataLoaderConfig {
|
|
11
|
+
[type: string]: Array<{ parse: (content: string) => unknown }>
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
/**
|
|
6
15
|
* FileObject encapsulates metadata and operations for a file, providing intelligent
|
|
@@ -60,7 +69,7 @@ import DirectoryObject from './DirectoryObject.js'
|
|
|
60
69
|
*
|
|
61
70
|
* for (const fileItem of files) {
|
|
62
71
|
* console.log(`${fileItem.module}${fileItem.extension} -> ${fileItem.path}`)
|
|
63
|
-
*
|
|
72
|
+
*
|
|
64
73
|
* // Type-based processing
|
|
65
74
|
* switch (fileItem.extension) {
|
|
66
75
|
* case '.json':
|
|
@@ -99,7 +108,13 @@ import DirectoryObject from './DirectoryObject.js'
|
|
|
99
108
|
* inherits the directory's resolved path, ensuring consistency in hierarchical
|
|
100
109
|
* file operations.
|
|
101
110
|
*/
|
|
102
|
-
export default class FileObject {
|
|
111
|
+
export default class FileObject extends FS {
|
|
112
|
+
/**
|
|
113
|
+
* Configuration for data parsing in the loadData method.
|
|
114
|
+
* Maps data type names to arrays of parser functions.
|
|
115
|
+
*/
|
|
116
|
+
static readonly dataLoaderConfig: DataLoaderConfig
|
|
117
|
+
|
|
103
118
|
/**
|
|
104
119
|
* Create a new FileObject instance with intelligent path resolution.
|
|
105
120
|
*
|
|
@@ -142,8 +157,8 @@ export default class FileObject {
|
|
|
142
157
|
* // Complex directory structures and nested files
|
|
143
158
|
* const projectRoot = new DirectoryObject('./my-project')
|
|
144
159
|
* const srcDir = new DirectoryObject('src', projectRoot)
|
|
145
|
-
*
|
|
146
|
-
* // Create files within nested directory structure
|
|
160
|
+
*
|
|
161
|
+
* // Create files within nested directory structure
|
|
147
162
|
* const mainApp = new FileObject('App.tsx', srcDir)
|
|
148
163
|
* const stylesheet = new FileObject('styles/main.css', srcDir)
|
|
149
164
|
* const testFile = new FileObject('__tests__/App.test.tsx', srcDir)
|
|
@@ -151,7 +166,7 @@ export default class FileObject {
|
|
|
151
166
|
* console.log('Main app:', mainApp.path) // /absolute/path/my-project/src/App.tsx
|
|
152
167
|
* console.log('Stylesheet:', stylesheet.path) // /absolute/path/my-project/src/styles/main.css
|
|
153
168
|
* console.log('Test file:', testFile.path) // /absolute/path/my-project/src/__tests__/App.test.tsx
|
|
154
|
-
*
|
|
169
|
+
*
|
|
155
170
|
* // All files share the same parent directory reference
|
|
156
171
|
* console.log('Same parent?', mainApp.directory === srcDir) // true
|
|
157
172
|
* ```
|
|
@@ -160,7 +175,7 @@ export default class FileObject {
|
|
|
160
175
|
|
|
161
176
|
/**
|
|
162
177
|
* The original user-supplied path string used during construction.
|
|
163
|
-
*
|
|
178
|
+
*
|
|
164
179
|
* Preserves the exact path string passed to the constructor, including
|
|
165
180
|
* any relative path indicators (./, ../) or path separators. Useful
|
|
166
181
|
* for debugging, logging, or when you need to recreate the original
|
|
@@ -170,7 +185,7 @@ export default class FileObject {
|
|
|
170
185
|
* ```typescript
|
|
171
186
|
* const file1 = new FileObject('./config.json')
|
|
172
187
|
* const file2 = new FileObject('../package.json')
|
|
173
|
-
*
|
|
188
|
+
*
|
|
174
189
|
* console.log(file1.supplied) // './config.json'
|
|
175
190
|
* console.log(file2.supplied) // '../package.json'
|
|
176
191
|
* console.log(file1.path) // '/absolute/path/to/config.json'
|
|
@@ -181,7 +196,7 @@ export default class FileObject {
|
|
|
181
196
|
|
|
182
197
|
/**
|
|
183
198
|
* The fully resolved absolute file path with normalized separators.
|
|
184
|
-
*
|
|
199
|
+
*
|
|
185
200
|
* Automatically resolved during construction using Node.js path utilities.
|
|
186
201
|
* Always uses forward slashes on Unix systems and backslashes on Windows.
|
|
187
202
|
* This is the canonical path that should be used for all file operations.
|
|
@@ -189,9 +204,9 @@ export default class FileObject {
|
|
|
189
204
|
* @example
|
|
190
205
|
* ```typescript
|
|
191
206
|
* // Different inputs, same resolved path
|
|
192
|
-
* const file1 = new FileObject('./src/../config.json')
|
|
207
|
+
* const file1 = new FileObject('./src/../config.json')
|
|
193
208
|
* const file2 = new FileObject('config.json')
|
|
194
|
-
*
|
|
209
|
+
*
|
|
195
210
|
* console.log(file1.path) // '/absolute/path/config.json'
|
|
196
211
|
* console.log(file2.path) // '/absolute/path/config.json'
|
|
197
212
|
* console.log(file1.path === file2.path) // true
|
|
@@ -201,7 +216,7 @@ export default class FileObject {
|
|
|
201
216
|
|
|
202
217
|
/**
|
|
203
218
|
* The file URI representation following RFC 3986 standard.
|
|
204
|
-
*
|
|
219
|
+
*
|
|
205
220
|
* Converts the absolute file path to a proper file:// URI scheme,
|
|
206
221
|
* handling URL encoding for special characters and proper formatting
|
|
207
222
|
* for cross-platform file URI access.
|
|
@@ -209,9 +224,9 @@ export default class FileObject {
|
|
|
209
224
|
* @example
|
|
210
225
|
* ```typescript
|
|
211
226
|
* const file = new FileObject('./my project/config file.json')
|
|
212
|
-
* console.log(file.uri)
|
|
227
|
+
* console.log(file.uri)
|
|
213
228
|
* // 'file:///absolute/path/my%20project/config%20file.json'
|
|
214
|
-
*
|
|
229
|
+
*
|
|
215
230
|
* // Can be used with URL constructor or file:// handlers
|
|
216
231
|
* const url = new URL(file.uri)
|
|
217
232
|
* console.log(url.pathname) // '/absolute/path/my project/config file.json'
|
|
@@ -221,7 +236,7 @@ export default class FileObject {
|
|
|
221
236
|
|
|
222
237
|
/**
|
|
223
238
|
* The complete filename including extension.
|
|
224
|
-
*
|
|
239
|
+
*
|
|
225
240
|
* Extracted from the resolved path using Node.js path utilities.
|
|
226
241
|
* Includes the file extension but excludes any directory components.
|
|
227
242
|
*
|
|
@@ -229,7 +244,7 @@ export default class FileObject {
|
|
|
229
244
|
* ```typescript
|
|
230
245
|
* const jsFile = new FileObject('./src/components/Button.tsx')
|
|
231
246
|
* const configFile = new FileObject('../.env.production')
|
|
232
|
-
*
|
|
247
|
+
*
|
|
233
248
|
* console.log(jsFile.name) // 'Button.tsx'
|
|
234
249
|
* console.log(configFile.name) // '.env.production'
|
|
235
250
|
* ```
|
|
@@ -238,7 +253,7 @@ export default class FileObject {
|
|
|
238
253
|
|
|
239
254
|
/**
|
|
240
255
|
* The filename without its extension, suitable for module identification.
|
|
241
|
-
*
|
|
256
|
+
*
|
|
242
257
|
* Useful for generating module names, import statements, or when you need
|
|
243
258
|
* the base name without file type information. Handles complex extensions
|
|
244
259
|
* and dotfiles appropriately.
|
|
@@ -247,7 +262,7 @@ export default class FileObject {
|
|
|
247
262
|
|
|
248
263
|
/**
|
|
249
264
|
* The file extension including the leading dot.
|
|
250
|
-
*
|
|
265
|
+
*
|
|
251
266
|
* Extracted using Node.js path utilities, always includes the dot prefix.
|
|
252
267
|
* Returns an empty string for files without extensions. Handles multiple
|
|
253
268
|
* extensions by returning only the last one.
|
|
@@ -262,7 +277,7 @@ export default class FileObject {
|
|
|
262
277
|
|
|
263
278
|
/**
|
|
264
279
|
* The parent DirectoryObject containing this file.
|
|
265
|
-
*
|
|
280
|
+
*
|
|
266
281
|
* Automatically created during FileObject construction based on the resolved
|
|
267
282
|
* file path. Provides access to parent directory operations and maintains
|
|
268
283
|
* the hierarchical relationship between files and directories.
|
|
@@ -271,7 +286,7 @@ export default class FileObject {
|
|
|
271
286
|
|
|
272
287
|
/**
|
|
273
288
|
* Promise that resolves to whether the file exists on the filesystem.
|
|
274
|
-
*
|
|
289
|
+
*
|
|
275
290
|
* Performs an asynchronous filesystem check to determine file existence.
|
|
276
291
|
* The Promise will resolve to true if the file exists and is accessible,
|
|
277
292
|
* false otherwise. Always await this property before using the result.
|
|
@@ -290,4 +305,25 @@ export default class FileObject {
|
|
|
290
305
|
isDirectory: boolean
|
|
291
306
|
directory: string | null
|
|
292
307
|
}
|
|
308
|
+
|
|
309
|
+
/** Check if a file can be read */
|
|
310
|
+
canRead(): Promise<boolean>
|
|
311
|
+
|
|
312
|
+
/** Check if a file can be written */
|
|
313
|
+
canWrite(): Promise<boolean>
|
|
314
|
+
|
|
315
|
+
/** Get the size of a file */
|
|
316
|
+
size(): Promise<number | null>
|
|
317
|
+
|
|
318
|
+
/** Get the last modification time of a file */
|
|
319
|
+
modified(): Promise<Date | null>
|
|
320
|
+
|
|
321
|
+
/** Read the content of a file */
|
|
322
|
+
read(encoding?: string): Promise<string>
|
|
323
|
+
|
|
324
|
+
/** Write content to a file */
|
|
325
|
+
write(content: string, encoding?: string): Promise<void>
|
|
326
|
+
|
|
327
|
+
/** Load an object from JSON5 or YAML file with type specification */
|
|
328
|
+
loadData(type?: 'json' | 'json5' | 'yaml' | 'any', encoding?: string): Promise<unknown>
|
|
293
329
|
}
|
package/src/types/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Core file system abstractions
|
|
3
3
|
export { default as FileObject } from './FileObject.js'
|
|
4
4
|
export { default as DirectoryObject } from './DirectoryObject.js'
|
|
5
|
-
export { default as
|
|
5
|
+
export { default as FS } from './FS.js'
|
|
6
6
|
|
|
7
7
|
// Utility classes
|
|
8
8
|
export { default as Cache } from './Cache.js'
|
|
@@ -15,4 +15,4 @@ export { default as Util } from './Util.js'
|
|
|
15
15
|
export { default as Valid } from './Valid.js'
|
|
16
16
|
|
|
17
17
|
// Type exports
|
|
18
|
-
export type { FileParts } from './
|
|
18
|
+
export type { FileParts } from './FS.js'
|