@gesslar/toolkit 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/index.js +3 -0
- package/src/lib/Action.js +283 -0
- package/src/lib/ActionRunner.js +21 -51
- package/src/lib/Contract.js +257 -0
- package/src/lib/Data.js +1 -1
- package/src/lib/DirectoryObject.js +94 -18
- package/src/lib/FileObject.js +39 -19
- package/src/lib/Glog.js +3 -2
- package/src/lib/Hooks.js +194 -0
- package/src/lib/Piper.js +74 -100
- package/src/lib/Schemer.js +89 -0
- package/src/lib/Terms.js +74 -0
- package/src/types/Contract.d.ts +162 -0
- package/src/types/DirectoryObject.d.ts +65 -2
- package/src/types/FileObject.d.ts +38 -2
- package/src/types/Schemer.d.ts +179 -0
- package/src/types/Terms.d.ts +145 -0
- package/src/types/index.d.ts +3 -0
- package/src/lib/BaseActionManager.js +0 -246
- package/src/lib/BaseHookManager.js +0 -206
package/src/lib/Data.js
CHANGED
|
@@ -160,7 +160,7 @@ export default class Data {
|
|
|
160
160
|
// Special cases that need extra validation
|
|
161
161
|
switch(valueType) {
|
|
162
162
|
case "Number":
|
|
163
|
-
return
|
|
163
|
+
return type === "Number" && !isNaN(value) // Excludes NaN
|
|
164
164
|
default:
|
|
165
165
|
return valueType === type
|
|
166
166
|
}
|
|
@@ -48,6 +48,8 @@ export default class DirectoryObject extends FS {
|
|
|
48
48
|
extension: null,
|
|
49
49
|
isFile: false,
|
|
50
50
|
isDirectory: true,
|
|
51
|
+
trail: null,
|
|
52
|
+
sep: null,
|
|
51
53
|
})
|
|
52
54
|
|
|
53
55
|
/**
|
|
@@ -63,6 +65,8 @@ export default class DirectoryObject extends FS {
|
|
|
63
65
|
const fileUri = FS.pathToUri(absolutePath)
|
|
64
66
|
const filePath = FS.uriToPath(fileUri)
|
|
65
67
|
const baseName = path.basename(absolutePath) || "."
|
|
68
|
+
const trail = filePath.split(path.sep)
|
|
69
|
+
const sep = path.sep
|
|
66
70
|
|
|
67
71
|
this.#meta.supplied = fixedDir
|
|
68
72
|
this.#meta.path = filePath
|
|
@@ -70,6 +74,8 @@ export default class DirectoryObject extends FS {
|
|
|
70
74
|
this.#meta.name = baseName
|
|
71
75
|
this.#meta.extension = ""
|
|
72
76
|
this.#meta.module = baseName
|
|
77
|
+
this.#meta.trail = trail
|
|
78
|
+
this.#meta.sep = sep
|
|
73
79
|
|
|
74
80
|
Object.freeze(this.#meta)
|
|
75
81
|
}
|
|
@@ -173,6 +179,27 @@ export default class DirectoryObject extends FS {
|
|
|
173
179
|
return this.#meta.extension
|
|
174
180
|
}
|
|
175
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Returns the platform-specific path separator.
|
|
184
|
+
*
|
|
185
|
+
* @returns {string} The path separator ('/' on Unix, '\\' on Windows)
|
|
186
|
+
*/
|
|
187
|
+
get sep() {
|
|
188
|
+
return this.#meta.sep
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Returns the directory path split into segments.
|
|
193
|
+
*
|
|
194
|
+
* @returns {string[]} Array of path segments
|
|
195
|
+
* @example
|
|
196
|
+
* const dir = new DirectoryObject('/path/to/directory')
|
|
197
|
+
* console.log(dir.trail) // ['', 'path', 'to', 'directory']
|
|
198
|
+
*/
|
|
199
|
+
get trail() {
|
|
200
|
+
return this.#meta.trail
|
|
201
|
+
}
|
|
202
|
+
|
|
176
203
|
/**
|
|
177
204
|
* Returns false. Because this is a directory.
|
|
178
205
|
*
|
|
@@ -217,33 +244,29 @@ export default class DirectoryObject extends FS {
|
|
|
217
244
|
{withFileTypes: true}
|
|
218
245
|
)
|
|
219
246
|
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const stat = await fs.stat(fullPath)
|
|
224
|
-
|
|
225
|
-
return {dirent, stat, fullPath}
|
|
226
|
-
}),
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
const files = results
|
|
230
|
-
.filter(({stat}) => stat.isFile())
|
|
231
|
-
.map(({fullPath}) => new FileObject(fullPath))
|
|
247
|
+
const files = found
|
|
248
|
+
.filter(dirent => dirent.isFile())
|
|
249
|
+
.map(dirent => new FileObject(path.join(this.path, dirent.name)))
|
|
232
250
|
|
|
233
|
-
const directories =
|
|
234
|
-
.filter(
|
|
235
|
-
.map(
|
|
251
|
+
const directories = found
|
|
252
|
+
.filter(dirent => dirent.isDirectory())
|
|
253
|
+
.map(dirent => new DirectoryObject(path.join(this.path, dirent.name)))
|
|
236
254
|
|
|
237
255
|
return {files, directories}
|
|
238
256
|
}
|
|
239
257
|
|
|
240
258
|
/**
|
|
241
|
-
* Ensures a directory exists, creating it if necessary
|
|
259
|
+
* Ensures a directory exists, creating it if necessary.
|
|
260
|
+
* Gracefully handles the case where the directory already exists.
|
|
242
261
|
*
|
|
243
262
|
* @async
|
|
244
|
-
* @param {object} [options] -
|
|
263
|
+
* @param {object} [options] - Options to pass to fs.mkdir (e.g., {recursive: true, mode: 0o755})
|
|
245
264
|
* @returns {Promise<void>}
|
|
246
|
-
* @throws {Sass} If directory creation fails
|
|
265
|
+
* @throws {Sass} If directory creation fails for reasons other than already existing
|
|
266
|
+
* @example
|
|
267
|
+
* // Create directory recursively
|
|
268
|
+
* const dir = new DirectoryObject('./build/output')
|
|
269
|
+
* await dir.assureExists({recursive: true})
|
|
247
270
|
*/
|
|
248
271
|
async assureExists(options = {}) {
|
|
249
272
|
if(await this.exists)
|
|
@@ -252,7 +275,60 @@ export default class DirectoryObject extends FS {
|
|
|
252
275
|
try {
|
|
253
276
|
await fs.mkdir(this.path, options)
|
|
254
277
|
} catch(e) {
|
|
278
|
+
if(e.code === "EEXIST") {
|
|
279
|
+
// Directory already exists, ignore
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
|
|
255
283
|
throw Sass.new(`Unable to create directory '${this.path}': ${e.message}`)
|
|
256
284
|
}
|
|
257
285
|
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Private generator that walks up the directory tree.
|
|
289
|
+
*
|
|
290
|
+
* @private
|
|
291
|
+
* @generator
|
|
292
|
+
* @yields {DirectoryObject} Parent directory objects from current to root
|
|
293
|
+
*/
|
|
294
|
+
*#walkUp() {
|
|
295
|
+
if(!Array.isArray(this.trail))
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
const curr = structuredClone(this.trail)
|
|
299
|
+
|
|
300
|
+
while(curr.length > 0) {
|
|
301
|
+
const joined = curr.join(this.sep)
|
|
302
|
+
|
|
303
|
+
// Stop if we've reached an empty path (which would resolve to CWD)
|
|
304
|
+
if(joined === "" || joined === this.sep) {
|
|
305
|
+
// Yield the root and stop
|
|
306
|
+
yield new DirectoryObject(this.sep)
|
|
307
|
+
break
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
yield new DirectoryObject(joined)
|
|
311
|
+
curr.pop()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Generator that walks up the directory tree, yielding each parent directory.
|
|
317
|
+
* Starts from the current directory and yields each parent until reaching the root.
|
|
318
|
+
*
|
|
319
|
+
* @returns {object} Generator yielding parent DirectoryObject instances
|
|
320
|
+
* @example
|
|
321
|
+
* const dir = new DirectoryObject('/path/to/deep/directory')
|
|
322
|
+
* for(const parent of dir.walkUp) {
|
|
323
|
+
* console.log(parent.path)
|
|
324
|
+
* // /path/to/deep/directory
|
|
325
|
+
* // /path/to/deep
|
|
326
|
+
* // /path/to
|
|
327
|
+
* // /path
|
|
328
|
+
* // /
|
|
329
|
+
* }
|
|
330
|
+
*/
|
|
331
|
+
get walkUp() {
|
|
332
|
+
return this.#walkUp()
|
|
333
|
+
}
|
|
258
334
|
}
|
package/src/lib/FileObject.js
CHANGED
|
@@ -10,6 +10,7 @@ import path from "node:path"
|
|
|
10
10
|
import util from "node:util"
|
|
11
11
|
import YAML from "yaml"
|
|
12
12
|
|
|
13
|
+
import Data from "./Data.js"
|
|
13
14
|
import DirectoryObject from "./DirectoryObject.js"
|
|
14
15
|
import FS from "./FS.js"
|
|
15
16
|
import Sass from "./Sass.js"
|
|
@@ -87,13 +88,18 @@ export default class FileObject extends FS {
|
|
|
87
88
|
|
|
88
89
|
const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
directory
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
const directoryObject = (() => {
|
|
92
|
+
switch(Data.typeOf(directory)) {
|
|
93
|
+
case "String":
|
|
94
|
+
return new DirectoryObject(directory)
|
|
95
|
+
case "DirectoryObject":
|
|
96
|
+
return directory
|
|
97
|
+
default:
|
|
98
|
+
return new DirectoryObject(dir)
|
|
99
|
+
}
|
|
100
|
+
})()
|
|
95
101
|
|
|
96
|
-
const final = FS.resolvePath(
|
|
102
|
+
const final = FS.resolvePath(directoryObject.path ?? ".", fixedFile)
|
|
97
103
|
|
|
98
104
|
const resolved = final
|
|
99
105
|
const fileUri = FS.pathToUri(resolved)
|
|
@@ -104,10 +110,7 @@ export default class FileObject extends FS {
|
|
|
104
110
|
this.#meta.name = base
|
|
105
111
|
this.#meta.extension = ext
|
|
106
112
|
this.#meta.module = path.basename(this.supplied, this.extension)
|
|
107
|
-
|
|
108
|
-
const {dir: newDir} = this.#deconstructFilenameToParts(this.path)
|
|
109
|
-
|
|
110
|
-
this.#meta.directory = new DirectoryObject(newDir)
|
|
113
|
+
this.#meta.directory = directoryObject
|
|
111
114
|
|
|
112
115
|
Object.freeze(this.#meta)
|
|
113
116
|
}
|
|
@@ -363,29 +366,46 @@ export default class FileObject extends FS {
|
|
|
363
366
|
}
|
|
364
367
|
|
|
365
368
|
/**
|
|
366
|
-
* Writes content to a file
|
|
369
|
+
* Writes content to a file asynchronously.
|
|
370
|
+
* Validates that the parent directory exists before writing.
|
|
367
371
|
*
|
|
368
372
|
* @param {string} content - The content to write
|
|
369
|
-
* @param {string} encoding - The encoding in which to write
|
|
373
|
+
* @param {string} [encoding] - The encoding in which to write (default: "utf8")
|
|
370
374
|
* @returns {Promise<void>}
|
|
375
|
+
* @throws {Sass} If the file path is invalid or the parent directory doesn't exist
|
|
376
|
+
* @example
|
|
377
|
+
* const file = new FileObject('./output/data.json')
|
|
378
|
+
* await file.write(JSON.stringify({key: 'value'}))
|
|
371
379
|
*/
|
|
372
380
|
async write(content, encoding="utf8") {
|
|
373
381
|
if(!this.path)
|
|
374
382
|
throw Sass.new("No absolute path in file")
|
|
375
383
|
|
|
376
|
-
await
|
|
384
|
+
if(await this.directory.exists)
|
|
385
|
+
await fs.writeFile(this.path, content, encoding)
|
|
386
|
+
|
|
387
|
+
else
|
|
388
|
+
throw Sass.new(`Invalid directory, ${this.directory.uri}`)
|
|
377
389
|
}
|
|
378
390
|
|
|
379
391
|
/**
|
|
380
|
-
* Loads an object from JSON or YAML
|
|
392
|
+
* Loads an object from JSON or YAML file.
|
|
393
|
+
* Attempts to parse content as JSON5 first, then falls back to YAML if specified.
|
|
381
394
|
*
|
|
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
|
|
395
|
+
* @param {string} [type] - The expected type of data to parse ("json", "json5", "yaml", or "any")
|
|
396
|
+
* @param {string} [encoding] - The encoding to read the file as (default: "utf8")
|
|
397
|
+
* @returns {Promise<unknown>} The parsed data object
|
|
398
|
+
* @throws {Sass} If the content cannot be parsed or type is unsupported
|
|
399
|
+
* @example
|
|
400
|
+
* const configFile = new FileObject('./config.json5')
|
|
401
|
+
* const config = await configFile.loadData('json5')
|
|
402
|
+
*
|
|
403
|
+
* // Auto-detect format
|
|
404
|
+
* const data = await configFile.loadData('any')
|
|
385
405
|
*/
|
|
386
406
|
async loadData(type="any", encoding="utf8") {
|
|
387
407
|
const content = await this.read(encoding)
|
|
388
|
-
const normalizedType = type.
|
|
408
|
+
const normalizedType = type.toLowerCase()
|
|
389
409
|
const toTry = {
|
|
390
410
|
json5: [JSON5],
|
|
391
411
|
json: [JSON5],
|
|
@@ -394,7 +414,7 @@ export default class FileObject extends FS {
|
|
|
394
414
|
}[normalizedType]
|
|
395
415
|
|
|
396
416
|
if(!toTry) {
|
|
397
|
-
throw Sass.new(`Unsupported data type '${type}'. Supported types: json, json5, yaml
|
|
417
|
+
throw Sass.new(`Unsupported data type '${type}'. Supported types: json, json5, yaml.`)
|
|
398
418
|
}
|
|
399
419
|
|
|
400
420
|
for(const format of toTry) {
|
package/src/lib/Glog.js
CHANGED
package/src/lib/Hooks.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {setTimeout as timeout} from "timers/promises"
|
|
2
|
+
|
|
3
|
+
import FileObject from "./FileObject.js"
|
|
4
|
+
import Sass from "./Sass.js"
|
|
5
|
+
import Util from "./Util.js"
|
|
6
|
+
import Valid from "./Valid.js"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generic base class for managing hooks with configurable event types.
|
|
10
|
+
* Provides common functionality for hook registration, execution, and lifecycle management.
|
|
11
|
+
* Designed to be extended by specific implementations.
|
|
12
|
+
*/
|
|
13
|
+
export default class Hooks {
|
|
14
|
+
#hooksFile = null
|
|
15
|
+
#hooks = null
|
|
16
|
+
#actionKind = null
|
|
17
|
+
#timeout = 1000 // Default 1 second timeout
|
|
18
|
+
#debug = null
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new BaseHookManager instance.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} config - Configuration object
|
|
24
|
+
* @param {string} config.actionKind - Action identifier
|
|
25
|
+
* @param {FileObject} config.hooksFile - File object containing hooks with uri property
|
|
26
|
+
* @param {number} [config.hookTimeout] - Hook execution timeout in milliseconds
|
|
27
|
+
* @param {unknown} [config.hooks] - The hooks object
|
|
28
|
+
* @param {import('../types.js').DebugFunction} debug - Debug function from Glog.
|
|
29
|
+
*/
|
|
30
|
+
constructor({actionKind, hooksFile, hooks, hookTimeout = 1000}, debug) {
|
|
31
|
+
this.#actionKind = actionKind
|
|
32
|
+
this.#hooksFile = hooksFile
|
|
33
|
+
this.#hooks = hooks
|
|
34
|
+
this.#timeout = hookTimeout
|
|
35
|
+
this.#debug = debug
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets the action identifier.
|
|
40
|
+
*
|
|
41
|
+
* @returns {string} Action identifier or instance
|
|
42
|
+
*/
|
|
43
|
+
get actionKind() {
|
|
44
|
+
return this.#actionKind
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Gets the hooks file object.
|
|
49
|
+
*
|
|
50
|
+
* @returns {FileObject} File object containing hooks
|
|
51
|
+
*/
|
|
52
|
+
get hooksFile() {
|
|
53
|
+
return this.#hooksFile
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets the loaded hooks object.
|
|
58
|
+
*
|
|
59
|
+
* @returns {object|null} Hooks object or null if not loaded
|
|
60
|
+
*/
|
|
61
|
+
get hooks() {
|
|
62
|
+
return this.#hooks
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Gets the hook execution timeout in milliseconds.
|
|
67
|
+
*
|
|
68
|
+
* @returns {number} Timeout in milliseconds
|
|
69
|
+
*/
|
|
70
|
+
get timeout() {
|
|
71
|
+
return this.#timeout
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the setup hook function if available.
|
|
76
|
+
*
|
|
77
|
+
* @returns {(args: object) => unknown|null} Setup hook function or null
|
|
78
|
+
*/
|
|
79
|
+
get setup() {
|
|
80
|
+
return this.hooks?.setup || null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Gets the cleanup hook function if available.
|
|
85
|
+
*
|
|
86
|
+
* @returns {(args: object) => unknown|null} Cleanup hook function or null
|
|
87
|
+
*/
|
|
88
|
+
get cleanup() {
|
|
89
|
+
return this.hooks?.cleanup || null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Static factory method to create and initialize a hook manager.
|
|
94
|
+
* Loads hooks from the specified file and returns an initialized instance.
|
|
95
|
+
* Override loadHooks() in subclasses to customize hook loading logic.
|
|
96
|
+
*
|
|
97
|
+
* @param {object} config - Same configuration object as constructor
|
|
98
|
+
* @param {string|object} config.actionKind - Action identifier or instance
|
|
99
|
+
* @param {FileObject} config.hooksFile - File object containing hooks with uri property
|
|
100
|
+
* @param {number} [config.timeOut] - Hook execution timeout in milliseconds
|
|
101
|
+
* @param {import('../types.js').DebugFunction} debug - The debug function.
|
|
102
|
+
* @returns {Promise<Hooks|null>} Initialized hook manager or null if no hooks found
|
|
103
|
+
*/
|
|
104
|
+
static async new(config, debug) {
|
|
105
|
+
debug("Creating new HookManager instance with args: %o", 2, config)
|
|
106
|
+
|
|
107
|
+
const instance = new this(config, debug)
|
|
108
|
+
const hooksFile = instance.hooksFile
|
|
109
|
+
|
|
110
|
+
debug("Loading hooks from %o", 2, hooksFile.uri)
|
|
111
|
+
|
|
112
|
+
debug("Checking hooks file exists: %o", 2, hooksFile.uri)
|
|
113
|
+
if(!await hooksFile.exists)
|
|
114
|
+
throw Sass.new(`No such hooks file, ${hooksFile.uri}`)
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const hooksImport = await hooksFile.import()
|
|
118
|
+
|
|
119
|
+
if(!hooksImport)
|
|
120
|
+
return null
|
|
121
|
+
|
|
122
|
+
debug("Hooks file imported successfully as a module", 2)
|
|
123
|
+
|
|
124
|
+
const actionKind = instance.actionKind
|
|
125
|
+
if(!hooksImport[actionKind])
|
|
126
|
+
return null
|
|
127
|
+
|
|
128
|
+
const hooks = new hooksImport[actionKind]({debug})
|
|
129
|
+
|
|
130
|
+
debug(hooks.constructor.name, 4)
|
|
131
|
+
|
|
132
|
+
// Attach common properties to hooks
|
|
133
|
+
instance.#hooks = hooks
|
|
134
|
+
|
|
135
|
+
debug("Hooks %o loaded successfully for %o", 2, hooksFile.uri, instance.actionKind)
|
|
136
|
+
|
|
137
|
+
return instance
|
|
138
|
+
} catch(error) {
|
|
139
|
+
debug("Failed to load hooks %o: %o", 1, hooksFile.uri, error.message)
|
|
140
|
+
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async callHook(kind, activityName, context) {
|
|
146
|
+
try {
|
|
147
|
+
const debug = this.#debug
|
|
148
|
+
const hooks = this.#hooks
|
|
149
|
+
|
|
150
|
+
if(!hooks)
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
const hookName = `${kind}$${activityName}`
|
|
154
|
+
|
|
155
|
+
debug("Looking for hook: %o", 4, hookName)
|
|
156
|
+
|
|
157
|
+
const hook = hooks[hookName]
|
|
158
|
+
if(!hook)
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
debug("Triggering hook: %o", 4, hookName)
|
|
162
|
+
Valid.type(hook, "Function", `Hook "${hookName}" is not a function`)
|
|
163
|
+
|
|
164
|
+
const hookFunction = async() => {
|
|
165
|
+
debug("Hook function starting execution: %o", 4, hookName)
|
|
166
|
+
|
|
167
|
+
const duration = (await Util.time(() => hook.call(this.#hooks, context))).cost
|
|
168
|
+
|
|
169
|
+
debug("Hook function completed successfully: %o, after %oms", 4, hookName, duration)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const hookTimeout = this.timeout
|
|
173
|
+
const expireAsync = (async() => {
|
|
174
|
+
await timeout(hookTimeout)
|
|
175
|
+
throw Sass.new(`Hook ${hookName} execution exceeded timeout of ${hookTimeout}ms`)
|
|
176
|
+
})()
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
debug("Starting Promise race for hook: %o", 4, hookName)
|
|
180
|
+
await Util.race([
|
|
181
|
+
hookFunction(),
|
|
182
|
+
expireAsync
|
|
183
|
+
])
|
|
184
|
+
} catch(error) {
|
|
185
|
+
throw Sass.new(`Processing hook ${kind}$${activityName}`, error)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
debug("We made it throoough the wildernessss", 4)
|
|
189
|
+
|
|
190
|
+
} catch(error) {
|
|
191
|
+
throw Sass.new(`Processing hook ${kind}$${activityName}`, error)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|