@gesslar/toolkit 0.0.13 → 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 +4 -13
- package/src/lib/Cache.js +3 -4
- package/src/lib/Data.js +31 -24
- package/src/lib/DirectoryObject.js +7 -4
- package/src/lib/FS.js +19 -15
- package/src/lib/FileObject.js +32 -7
- 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/FileObject.d.ts +15 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gesslar/toolkit",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Get in, bitches, we're going toolkitting.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -21,18 +21,11 @@
|
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"lint": "eslint src/",
|
|
24
|
-
"lint:docs": "cd docs && npm run lint",
|
|
25
24
|
"lint:fix": "eslint src/ --fix",
|
|
26
|
-
"lint:fix:docs": "cd docs && npm run lint:fix",
|
|
27
25
|
"submit": "npm publish --access public",
|
|
28
26
|
"update": "npx npm-check-updates -u && npm install",
|
|
29
|
-
"test": "node
|
|
30
|
-
"
|
|
31
|
-
"docs:build": "npm run docs:clean && npm run docs:generate && cd docs && npm run build",
|
|
32
|
-
"docs:serve": "cd docs && npm run serve",
|
|
33
|
-
"docs:clean": "cd docs && npm run docs:clean",
|
|
34
|
-
"docs:generate": "npx typedoc src/types/index.d.ts --out docs/docs/api --plugin typedoc-plugin-markdown --readme none --excludePrivate --excludeProtected --excludeInternal --includeVersion",
|
|
35
|
-
"docs:install": "cd docs && npm install"
|
|
27
|
+
"test": "node --test tests/unit/*.test.js",
|
|
28
|
+
"test:unit": "node --test tests/unit/*.test.js"
|
|
36
29
|
},
|
|
37
30
|
"repository": {
|
|
38
31
|
"type": "git",
|
|
@@ -66,8 +59,6 @@
|
|
|
66
59
|
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
|
67
60
|
"@typescript-eslint/parser": "^8.44.0",
|
|
68
61
|
"eslint": "^9.36.0",
|
|
69
|
-
"eslint-plugin-jsdoc": "^60.1.0"
|
|
70
|
-
"typedoc": "^0.28.13",
|
|
71
|
-
"typedoc-plugin-markdown": "^4.9.0"
|
|
62
|
+
"eslint-plugin-jsdoc": "^60.1.0"
|
|
72
63
|
}
|
|
73
64
|
}
|
package/src/lib/Cache.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import File from "./FS.js"
|
|
2
1
|
import FileObject from "./FileObject.js"
|
|
3
2
|
import Sass from "./Sass.js"
|
|
4
3
|
|
|
@@ -41,11 +40,11 @@ export default class Cache {
|
|
|
41
40
|
* parallel processing.
|
|
42
41
|
*
|
|
43
42
|
* @param {FileObject} fileObject - The file object to load and cache
|
|
44
|
-
* @returns {Promise<
|
|
43
|
+
* @returns {Promise<unknown>} The parsed file data (JSON5 or YAML)
|
|
45
44
|
* @throws {Sass} If the file cannot be found or accessed
|
|
46
45
|
*/
|
|
47
46
|
async loadCachedData(fileObject) {
|
|
48
|
-
const lastModified = await
|
|
47
|
+
const lastModified = await fileObject.modified()
|
|
49
48
|
|
|
50
49
|
if(lastModified === null)
|
|
51
50
|
throw Sass.new(`Unable to find file '${fileObject.path}'`)
|
|
@@ -64,7 +63,7 @@ export default class Cache {
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
const data = await
|
|
66
|
+
const data = await fileObject.loadData()
|
|
68
67
|
|
|
69
68
|
this.#modifiedTimes.set(fileObject.path, lastModified)
|
|
70
69
|
this.#dataCache.set(fileObject.path, data)
|
package/src/lib/Data.js
CHANGED
|
@@ -20,6 +20,7 @@ export default class Data {
|
|
|
20
20
|
static primitives = Object.freeze([
|
|
21
21
|
// Primitives
|
|
22
22
|
"undefined",
|
|
23
|
+
"null",
|
|
23
24
|
"boolean",
|
|
24
25
|
"number",
|
|
25
26
|
"bigint",
|
|
@@ -196,10 +197,18 @@ export default class Data {
|
|
|
196
197
|
const result = {}
|
|
197
198
|
|
|
198
199
|
for(const [key, value] of Object.entries(obj)) {
|
|
199
|
-
if(Data.isType(value, "
|
|
200
|
+
if(Data.isType(value, "array")) {
|
|
201
|
+
// Clone arrays by mapping over them
|
|
202
|
+
result[key] = value.map(item =>
|
|
203
|
+
Data.isType(item, "object") || Data.isType(item, "array")
|
|
204
|
+
? Data.cloneObject(item)
|
|
205
|
+
: item
|
|
206
|
+
)
|
|
207
|
+
} else if(Data.isType(value, "object")) {
|
|
200
208
|
result[key] = Data.cloneObject(value)
|
|
201
|
-
else
|
|
209
|
+
} else {
|
|
202
210
|
result[key] = value
|
|
211
|
+
}
|
|
203
212
|
}
|
|
204
213
|
|
|
205
214
|
return freeze ? Object.freeze(result) : result
|
|
@@ -340,30 +349,16 @@ export default class Data {
|
|
|
340
349
|
return false
|
|
341
350
|
|
|
342
351
|
const valueType = Data.typeOf(value)
|
|
352
|
+
const normalizedType = type.toLowerCase()
|
|
343
353
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return Array.isArray(value) // Native array check
|
|
347
|
-
case "string":
|
|
348
|
-
return valueType === "string"
|
|
349
|
-
case "boolean":
|
|
350
|
-
return valueType === "boolean"
|
|
354
|
+
// Special cases that need extra validation
|
|
355
|
+
switch(normalizedType) {
|
|
351
356
|
case "number":
|
|
352
357
|
return valueType === "number" && !isNaN(value) // Excludes NaN
|
|
353
358
|
case "object":
|
|
354
|
-
return
|
|
355
|
-
case "function":
|
|
356
|
-
return valueType === "function"
|
|
357
|
-
case "symbol":
|
|
358
|
-
return valueType === "symbol" // ES6 Symbol type
|
|
359
|
-
case "bigint":
|
|
360
|
-
return valueType === "bigint" // BigInt support
|
|
361
|
-
case "null":
|
|
362
|
-
return value === null // Explicit null check
|
|
363
|
-
case "undefined":
|
|
364
|
-
return valueType === "undefined" // Explicit undefined check
|
|
359
|
+
return valueType === "object" && value !== null && !Array.isArray(value) // Excludes arrays and null
|
|
365
360
|
default:
|
|
366
|
-
return
|
|
361
|
+
return valueType === normalizedType
|
|
367
362
|
}
|
|
368
363
|
}
|
|
369
364
|
|
|
@@ -374,7 +369,13 @@ export default class Data {
|
|
|
374
369
|
* @returns {string} The type of the value
|
|
375
370
|
*/
|
|
376
371
|
static typeOf(value) {
|
|
377
|
-
|
|
372
|
+
if(value === null)
|
|
373
|
+
return "null"
|
|
374
|
+
|
|
375
|
+
if(Array.isArray(value))
|
|
376
|
+
return "array"
|
|
377
|
+
|
|
378
|
+
return typeof value
|
|
378
379
|
}
|
|
379
380
|
|
|
380
381
|
/**
|
|
@@ -397,11 +398,16 @@ export default class Data {
|
|
|
397
398
|
* @returns {boolean} Whether the value is empty
|
|
398
399
|
*/
|
|
399
400
|
static isEmpty(value, checkForNothing = true) {
|
|
400
|
-
const type = Data.typeOf(value)
|
|
401
|
-
|
|
402
401
|
if(checkForNothing && Data.isNothing(value))
|
|
403
402
|
return true
|
|
404
403
|
|
|
404
|
+
// When checkForNothing is false, null/undefined should not be treated as empty
|
|
405
|
+
// They should be processed like regular values
|
|
406
|
+
if(!checkForNothing && Data.isNothing(value))
|
|
407
|
+
return false
|
|
408
|
+
|
|
409
|
+
const type = Data.typeOf(value)
|
|
410
|
+
|
|
405
411
|
if(!Data.emptyableTypes.includes(type))
|
|
406
412
|
return false
|
|
407
413
|
|
|
@@ -409,6 +415,7 @@ export default class Data {
|
|
|
409
415
|
case "array":
|
|
410
416
|
return value.length === 0
|
|
411
417
|
case "object":
|
|
418
|
+
// null was already handled above, so this should only be real objects
|
|
412
419
|
return Object.keys(value).length === 0
|
|
413
420
|
case "string":
|
|
414
421
|
return value.trim().length === 0
|
|
@@ -48,7 +48,6 @@ export default class DirectoryObject extends FS {
|
|
|
48
48
|
extension: null,
|
|
49
49
|
isFile: false,
|
|
50
50
|
isDirectory: true,
|
|
51
|
-
directory: null,
|
|
52
51
|
})
|
|
53
52
|
|
|
54
53
|
/**
|
|
@@ -57,7 +56,7 @@ export default class DirectoryObject extends FS {
|
|
|
57
56
|
* @param {string} directory - The directory path
|
|
58
57
|
*/
|
|
59
58
|
constructor(directory) {
|
|
60
|
-
super(
|
|
59
|
+
super()
|
|
61
60
|
|
|
62
61
|
const fixedDir = FS.fixSlashes(directory ?? ".")
|
|
63
62
|
const absolutePath = path.resolve(fixedDir)
|
|
@@ -214,10 +213,14 @@ export default class DirectoryObject extends FS {
|
|
|
214
213
|
* @returns {Promise<{files: Array<FileObject>, directories: Array<DirectoryObject>}>} The files and directories in the directory.
|
|
215
214
|
*/
|
|
216
215
|
async read(directory) {
|
|
217
|
-
const found = await fs.readdir(
|
|
216
|
+
const found = await fs.readdir(
|
|
217
|
+
new URL(directory.uri),
|
|
218
|
+
{withFileTypes: true}
|
|
219
|
+
)
|
|
220
|
+
|
|
218
221
|
const results = await Promise.all(
|
|
219
222
|
found.map(async dirent => {
|
|
220
|
-
const fullPath = path.join(directory.
|
|
223
|
+
const fullPath = path.join(directory.path, dirent.name)
|
|
221
224
|
const stat = await fs.stat(fullPath)
|
|
222
225
|
|
|
223
226
|
return {dirent, stat, fullPath}
|
package/src/lib/FS.js
CHANGED
|
@@ -123,6 +123,7 @@ export default class FS {
|
|
|
123
123
|
* @returns {string} The merged path
|
|
124
124
|
*/
|
|
125
125
|
static mergeOverlappingPaths(path1, path2, sep=path.sep) {
|
|
126
|
+
const isAbsolutePath1 = path.isAbsolute(path1)
|
|
126
127
|
const from = path1.split(sep).filter(Boolean)
|
|
127
128
|
const to = path2.split(sep).filter(Boolean)
|
|
128
129
|
|
|
@@ -131,18 +132,17 @@ export default class FS {
|
|
|
131
132
|
return path1
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
const overlapIndex = from.findLastIndex(
|
|
135
|
-
const left = from.at(index)
|
|
136
|
-
const right = to.at(0)
|
|
137
|
-
|
|
138
|
-
return left === right
|
|
139
|
-
})
|
|
135
|
+
const overlapIndex = from.findLastIndex(curr => curr === to.at(0))
|
|
140
136
|
|
|
141
137
|
// If overlap is found, slice and join
|
|
142
138
|
if(overlapIndex !== -1) {
|
|
143
139
|
const prefix = from.slice(0, overlapIndex)
|
|
140
|
+
const result = path.join(...prefix, ...to)
|
|
144
141
|
|
|
145
|
-
|
|
142
|
+
// If original path1 was absolute, ensure result is also absolute
|
|
143
|
+
return isAbsolutePath1 && !path.isAbsolute(result)
|
|
144
|
+
? path.sep + result
|
|
145
|
+
: result
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// If no overlap, just join the paths
|
|
@@ -159,8 +159,8 @@ export default class FS {
|
|
|
159
159
|
*/
|
|
160
160
|
static resolvePath(fromPath, toPath) {
|
|
161
161
|
// Normalize inputs
|
|
162
|
-
const from = fromPath
|
|
163
|
-
const to = toPath
|
|
162
|
+
const from = fromPath?.trim() ?? ""
|
|
163
|
+
const to = toPath?.trim() ?? ""
|
|
164
164
|
|
|
165
165
|
// Handle empty cases
|
|
166
166
|
if(!from && !to)
|
|
@@ -172,16 +172,20 @@ export default class FS {
|
|
|
172
172
|
if(!to)
|
|
173
173
|
return from
|
|
174
174
|
|
|
175
|
+
const normalizedTo = /^\.\//.test(to)
|
|
176
|
+
? path.normalize(to)
|
|
177
|
+
: to
|
|
178
|
+
|
|
175
179
|
// Strategy 1: If 'to' is absolute, it's standalone
|
|
176
|
-
if(path.isAbsolute(
|
|
177
|
-
return
|
|
180
|
+
if(path.isAbsolute(normalizedTo))
|
|
181
|
+
return normalizedTo
|
|
178
182
|
|
|
179
|
-
// Strategy 2: If 'to' contains relative navigation
|
|
180
|
-
if(to.
|
|
181
|
-
return path.resolve(from,
|
|
183
|
+
// Strategy 2: If 'to' contains relative navigation
|
|
184
|
+
if(to.startsWith("../"))
|
|
185
|
+
return path.resolve(from, normalizedTo)
|
|
182
186
|
|
|
183
187
|
// Strategy 3: Try overlap-based merging, which will default to a basic
|
|
184
188
|
// join if no overlap
|
|
185
|
-
return FS.mergeOverlappingPaths(from,
|
|
189
|
+
return FS.mergeOverlappingPaths(from, normalizedTo)
|
|
186
190
|
}
|
|
187
191
|
}
|
package/src/lib/FileObject.js
CHANGED
|
@@ -32,6 +32,19 @@ import Valid from "./Valid.js"
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
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
|
+
|
|
35
48
|
/**
|
|
36
49
|
* @type {object}
|
|
37
50
|
* @private
|
|
@@ -64,18 +77,23 @@ export default class FileObject extends FS {
|
|
|
64
77
|
* @param {DirectoryObject|string|null} [directory] - The parent directory (object or string)
|
|
65
78
|
*/
|
|
66
79
|
constructor(fileName, directory=null) {
|
|
67
|
-
super(
|
|
80
|
+
super()
|
|
81
|
+
|
|
82
|
+
if(!fileName || typeof fileName !== "string" || fileName.length === 0) {
|
|
83
|
+
throw Sass.new("fileName must be a non-empty string")
|
|
84
|
+
}
|
|
68
85
|
|
|
69
86
|
const fixedFile = FS.fixSlashes(fileName)
|
|
70
87
|
|
|
71
88
|
const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
|
|
72
89
|
|
|
73
|
-
if(!directory)
|
|
90
|
+
if(!directory) {
|
|
74
91
|
directory = new DirectoryObject(dir)
|
|
92
|
+
} else if(typeof directory === "string") {
|
|
93
|
+
directory = new DirectoryObject(directory)
|
|
94
|
+
}
|
|
75
95
|
|
|
76
|
-
const final = path.
|
|
77
|
-
? fixedFile
|
|
78
|
-
: path.resolve(directory?.path ?? ".", fixedFile)
|
|
96
|
+
const final = FS.resolvePath(directory?.path ?? ".", fixedFile)
|
|
79
97
|
|
|
80
98
|
const resolved = final
|
|
81
99
|
const fileUri = FS.pathToUri(resolved)
|
|
@@ -99,6 +117,9 @@ export default class FileObject extends FS {
|
|
|
99
117
|
*
|
|
100
118
|
* @returns {string} string representation of the FileObject
|
|
101
119
|
*/
|
|
120
|
+
toString() {
|
|
121
|
+
return `[FileObject: ${this.path}]`
|
|
122
|
+
}
|
|
102
123
|
|
|
103
124
|
/**
|
|
104
125
|
* Returns a JSON representation of the FileObject.
|
|
@@ -360,7 +381,7 @@ export default class FileObject extends FS {
|
|
|
360
381
|
*
|
|
361
382
|
* @param {string} [type] - The expected type of data to parse.
|
|
362
383
|
* @param {string} [encoding] - The encoding to read the file as.
|
|
363
|
-
* @returns {
|
|
384
|
+
* @returns {Promise<unknown>} The parsed data object.
|
|
364
385
|
*/
|
|
365
386
|
async loadData(type="any", encoding="utf8") {
|
|
366
387
|
const content = await this.read(encoding)
|
|
@@ -371,7 +392,11 @@ export default class FileObject extends FS {
|
|
|
371
392
|
any: [JSON5,YAML]
|
|
372
393
|
}[type.toLowerCase()]
|
|
373
394
|
|
|
374
|
-
|
|
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) {
|
|
375
400
|
try {
|
|
376
401
|
const result = format.parse(content)
|
|
377
402
|
|
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
|
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
import DirectoryObject from './DirectoryObject.js'
|
|
4
4
|
import FS from './FS.js'
|
|
5
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
|
+
}
|
|
13
|
+
|
|
6
14
|
/**
|
|
7
15
|
* FileObject encapsulates metadata and operations for a file, providing intelligent
|
|
8
16
|
* path resolution, metadata extraction, and file system operations. This class serves
|
|
@@ -101,6 +109,12 @@ import FS from './FS.js'
|
|
|
101
109
|
* file operations.
|
|
102
110
|
*/
|
|
103
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
|
+
|
|
104
118
|
/**
|
|
105
119
|
* Create a new FileObject instance with intelligent path resolution.
|
|
106
120
|
*
|
|
@@ -311,5 +325,5 @@ export default class FileObject extends FS {
|
|
|
311
325
|
write(content: string, encoding?: string): Promise<void>
|
|
312
326
|
|
|
313
327
|
/** Load an object from JSON5 or YAML file with type specification */
|
|
314
|
-
loadData(type?: 'json' | 'json5' | 'yaml' | 'any', encoding?: string): Promise<
|
|
328
|
+
loadData(type?: 'json' | 'json5' | 'yaml' | 'any', encoding?: string): Promise<unknown>
|
|
315
329
|
}
|