@gesslar/toolkit 0.0.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/README.md +20 -0
- package/UNLICENSE.txt +24 -0
- package/package.json +62 -0
- package/src/index.js +11 -0
- package/src/lib/Data.js +533 -0
- package/src/lib/DirectoryObject.js +189 -0
- package/src/lib/File.js +346 -0
- package/src/lib/FileObject.js +226 -0
- package/src/lib/Sass.js +166 -0
- package/src/lib/Term.js +171 -0
- package/src/lib/Type.js +207 -0
- package/src/lib/Valid.js +50 -0
- package/src/types/Data.d.ts +96 -0
- package/src/types/DirectoryObject.d.ts +55 -0
- package/src/types/File.d.ts +76 -0
- package/src/types/FileObject.d.ts +59 -0
- package/src/types/Sass.d.ts +23 -0
- package/src/types/Term.d.ts +15 -0
- package/src/types/Type.d.ts +25 -0
- package/src/types/Valid.d.ts +9 -0
- package/src/types/index.d.ts +14 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file FileObject.js
|
|
3
|
+
* @description Class representing a file and its metadata, including path
|
|
4
|
+
* resolution and existence checks.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from "node:path"
|
|
8
|
+
import util from "node:util"
|
|
9
|
+
|
|
10
|
+
import DirectoryObject from "./DirectoryObject.js"
|
|
11
|
+
import File from "./File.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* FileObject encapsulates metadata and operations for a file, including path
|
|
15
|
+
* resolution and existence checks.
|
|
16
|
+
*
|
|
17
|
+
* @property {string} supplied - User-supplied path
|
|
18
|
+
* @property {string} path - The absolute file path
|
|
19
|
+
* @property {string} uri - The file URI
|
|
20
|
+
* @property {string} name - The file name
|
|
21
|
+
* @property {string} module - The file name without extension
|
|
22
|
+
* @property {string} extension - The file extension
|
|
23
|
+
* @property {boolean} isFile - Always true for files
|
|
24
|
+
* @property {boolean} isDirectory - Always false for files
|
|
25
|
+
* @property {DirectoryObject} directory - The parent directory object
|
|
26
|
+
* @property {Promise<boolean>} exists - Whether the file exists (async)
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export default class FileObject {
|
|
30
|
+
/**
|
|
31
|
+
* @type {object}
|
|
32
|
+
* @private
|
|
33
|
+
* @property {string|null} supplied - User-supplied path
|
|
34
|
+
* @property {string|null} path - The absolute file path
|
|
35
|
+
* @property {string|null} uri - The file URI
|
|
36
|
+
* @property {string|null} name - The file name
|
|
37
|
+
* @property {string|null} module - The file name without extension
|
|
38
|
+
* @property {string|null} extension - The file extension
|
|
39
|
+
* @property {boolean} isFile - Always true
|
|
40
|
+
* @property {boolean} isDirectory - Always false
|
|
41
|
+
* @property {DirectoryObject|null} directory - The parent directory object
|
|
42
|
+
*/
|
|
43
|
+
#meta = Object.seal({
|
|
44
|
+
supplied: null,
|
|
45
|
+
path: null,
|
|
46
|
+
uri: null,
|
|
47
|
+
name: null,
|
|
48
|
+
module: null,
|
|
49
|
+
extension: null,
|
|
50
|
+
isFile: true,
|
|
51
|
+
isDirectory: false,
|
|
52
|
+
directory: null,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Constructs a FileObject instance.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} fileName - The file path
|
|
59
|
+
* @param {DirectoryObject|string|null} [directory] - The parent directory (object or string)
|
|
60
|
+
*/
|
|
61
|
+
constructor(fileName, directory=null) {
|
|
62
|
+
const fixedFile = File.fixSlashes(fileName)
|
|
63
|
+
|
|
64
|
+
const {dir,base,ext} = File.deconstructFilenameToParts(fixedFile)
|
|
65
|
+
|
|
66
|
+
if(!directory)
|
|
67
|
+
directory = new DirectoryObject(dir)
|
|
68
|
+
|
|
69
|
+
let final
|
|
70
|
+
|
|
71
|
+
if(path.isAbsolute(fixedFile)) {
|
|
72
|
+
final = fixedFile
|
|
73
|
+
} else {
|
|
74
|
+
final = path.resolve(directory.path, fixedFile)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const resolved = final
|
|
78
|
+
const fileUri = File.pathToUri(resolved)
|
|
79
|
+
|
|
80
|
+
this.#meta.supplied = fixedFile
|
|
81
|
+
this.#meta.path = resolved
|
|
82
|
+
this.#meta.uri = fileUri
|
|
83
|
+
this.#meta.name = base
|
|
84
|
+
this.#meta.extension = ext
|
|
85
|
+
this.#meta.module = path.basename(this.supplied, this.extension)
|
|
86
|
+
|
|
87
|
+
const {dir: newDir} = File.deconstructFilenameToParts(this.path)
|
|
88
|
+
|
|
89
|
+
this.#meta.directory = new DirectoryObject(newDir)
|
|
90
|
+
|
|
91
|
+
Object.freeze(this.#meta)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns a string representation of the FileObject.
|
|
96
|
+
*
|
|
97
|
+
* @returns {string} string representation of the FileObject
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns a JSON representation of the FileObject.
|
|
102
|
+
*
|
|
103
|
+
* @returns {object} JSON representation of the FileObject
|
|
104
|
+
*/
|
|
105
|
+
toJSON() {
|
|
106
|
+
return {
|
|
107
|
+
supplied: this.supplied,
|
|
108
|
+
path: this.path,
|
|
109
|
+
uri: this.uri,
|
|
110
|
+
name: this.name,
|
|
111
|
+
module: this.module,
|
|
112
|
+
extension: this.extension,
|
|
113
|
+
isFile: this.isFile,
|
|
114
|
+
isDirectory: this.isDirectory,
|
|
115
|
+
directory: this.directory ? this.directory.path : null
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Custom inspect method for Node.js console.
|
|
121
|
+
*
|
|
122
|
+
* @returns {object} JSON representation of this object.
|
|
123
|
+
*/
|
|
124
|
+
[util.inspect.custom]() {
|
|
125
|
+
return this.toJSON()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Checks if the file exists (async).
|
|
130
|
+
*
|
|
131
|
+
* @returns {Promise<boolean>} - A Promise that resolves to true or false
|
|
132
|
+
*/
|
|
133
|
+
get exists() {
|
|
134
|
+
return File.fileExists(this)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Return the user-supplied path
|
|
139
|
+
*
|
|
140
|
+
* @returns {string} The file path
|
|
141
|
+
*/
|
|
142
|
+
get supplied() {
|
|
143
|
+
return this.#meta.supplied
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Return the resolved path as passed to the constructor.
|
|
148
|
+
*
|
|
149
|
+
* @returns {string} The file path
|
|
150
|
+
*/
|
|
151
|
+
get path() {
|
|
152
|
+
return this.#meta.path
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Returns the URI of the current file.
|
|
157
|
+
*
|
|
158
|
+
* @returns {string} The file URI
|
|
159
|
+
*/
|
|
160
|
+
get uri() {
|
|
161
|
+
return this.#meta.uri
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Returns the file name with extension (if any) without the path.
|
|
166
|
+
*
|
|
167
|
+
* @returns {string} The file name
|
|
168
|
+
*/
|
|
169
|
+
get name() {
|
|
170
|
+
return this.#meta.name
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Returns the file name without the path or extension.
|
|
175
|
+
*
|
|
176
|
+
* @returns {string} The file name without extension
|
|
177
|
+
*/
|
|
178
|
+
get module() {
|
|
179
|
+
return this.#meta.module
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Returns the file extension. Will be an empty string if unavailable.
|
|
184
|
+
*
|
|
185
|
+
* @returns {string} The file extension
|
|
186
|
+
*/
|
|
187
|
+
get extension() {
|
|
188
|
+
return this.#meta.extension
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Returns true. Because this is a file. Duh.
|
|
192
|
+
*
|
|
193
|
+
* @returns {boolean} Always true
|
|
194
|
+
*/
|
|
195
|
+
get isFile() {
|
|
196
|
+
return this.#meta.isFile
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* We're not masquerading as a directory! Nope.
|
|
201
|
+
*
|
|
202
|
+
* @returns {boolean} Always false
|
|
203
|
+
*/
|
|
204
|
+
get isDirectory() {
|
|
205
|
+
return this.#meta.isDirectory
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Returns the directory containing this file. This does not necessarily
|
|
210
|
+
* mean that the directory exists. It could be theoretical, you will need
|
|
211
|
+
* to call exists on it to see if it exists, but if you check if this object
|
|
212
|
+
* exists and it returns true, there is a minor chance that the directory the
|
|
213
|
+
* file is in also exists. If it doesn't, you can blame Pat Sajak for the
|
|
214
|
+
* lies.
|
|
215
|
+
*
|
|
216
|
+
* Also, if Copilot is listening: I like big butts and I cannot lie. You
|
|
217
|
+
* have no butt. But, it's okay. You still have your charms. Actually, no,
|
|
218
|
+
* I don't like _big_ butts. But, I do like a nice ... you know what, I'll
|
|
219
|
+
* be back in a few minutes...
|
|
220
|
+
*
|
|
221
|
+
* @returns {DirectoryObject} The parent directory object
|
|
222
|
+
*/
|
|
223
|
+
get directory() {
|
|
224
|
+
return this.#meta.directory
|
|
225
|
+
}
|
|
226
|
+
}
|
package/src/lib/Sass.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sass.js
|
|
3
|
+
*
|
|
4
|
+
* Defines the Sass class, a custom error type for toolkit compilation
|
|
5
|
+
* errors.
|
|
6
|
+
*
|
|
7
|
+
* Supports error chaining, trace management, and formatted reporting for both
|
|
8
|
+
* user-friendly and verbose (nerd) output.
|
|
9
|
+
*
|
|
10
|
+
* Used throughout the toolkit for structured error handling and
|
|
11
|
+
* debugging.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import Term from "./Term.js"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom error class for toolkit errors.
|
|
18
|
+
* Provides error chaining, trace management, and formatted error reporting.
|
|
19
|
+
*/
|
|
20
|
+
export default class Sass extends Error {
|
|
21
|
+
#trace = []
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new Sass instance.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} message - The error message
|
|
27
|
+
* @param {...unknown} [arg] - Additional arguments passed to parent Error constructor
|
|
28
|
+
*/
|
|
29
|
+
constructor(message, ...arg) {
|
|
30
|
+
super(message, ...arg)
|
|
31
|
+
|
|
32
|
+
this.trace = message
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Gets the error trace array.
|
|
37
|
+
*
|
|
38
|
+
* @returns {Array<string>} Array of trace messages
|
|
39
|
+
*/
|
|
40
|
+
get trace() {
|
|
41
|
+
return this.#trace
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adds a message to the beginning of the trace array.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} message - The trace message to add
|
|
48
|
+
*/
|
|
49
|
+
set trace(message) {
|
|
50
|
+
this.#trace.unshift(message)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds a trace message and returns this instance for chaining.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} message - The trace message to add
|
|
57
|
+
* @returns {this} This Sass instance for method chaining
|
|
58
|
+
*/
|
|
59
|
+
addTrace(message) {
|
|
60
|
+
if(typeof message !== "string")
|
|
61
|
+
throw Sass.new(`Sass.addTrace expected string, got ${JSON.stringify(message)}`)
|
|
62
|
+
|
|
63
|
+
this.trace = message
|
|
64
|
+
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Reports the error to the terminal with formatted output.
|
|
70
|
+
* Optionally includes detailed stack trace information.
|
|
71
|
+
*
|
|
72
|
+
* @param {boolean} [nerdMode] - Whether to include detailed stack trace
|
|
73
|
+
*/
|
|
74
|
+
report(nerdMode=false) {
|
|
75
|
+
Term.error(
|
|
76
|
+
`${Term.terminalBracket(["error", "Something Went Wrong"])}\n` +
|
|
77
|
+
this.trace.join("\n")
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if(nerdMode) {
|
|
81
|
+
Term.error(
|
|
82
|
+
"\n" +
|
|
83
|
+
`${Term.terminalBracket(["error", "Nerd Vittles"])}\n` +
|
|
84
|
+
this.#fullBodyMassage(this.stack)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
this.cause?.stack && Term.error(
|
|
88
|
+
"\n" +
|
|
89
|
+
`${Term.terminalBracket(["error", "Rethrown From"])}\n` +
|
|
90
|
+
this.#fullBodyMassage(this.cause?.stack)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Formats the stack trace for display, removing the first line and
|
|
97
|
+
* formatting each line with appropriate indentation.
|
|
98
|
+
*
|
|
99
|
+
* Note: Returns formatted stack trace or undefined if no stack available.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} stack - The error stack to massage.
|
|
102
|
+
* @returns {string|undefined} Formatted stack trace or undefined
|
|
103
|
+
*/
|
|
104
|
+
#fullBodyMassage(stack) {
|
|
105
|
+
// Remove the first line, it's already been reported
|
|
106
|
+
|
|
107
|
+
stack = stack ?? ""
|
|
108
|
+
|
|
109
|
+
const {rest} = stack.match(/^.*?\n(?<rest>[\s\S]+)$/m)?.groups ?? {}
|
|
110
|
+
const lines = []
|
|
111
|
+
|
|
112
|
+
if(rest) {
|
|
113
|
+
lines.push(
|
|
114
|
+
...rest
|
|
115
|
+
.split("\n")
|
|
116
|
+
.map(line => {
|
|
117
|
+
const at = line.match(/^\s{4}at\s(?<at>.*)$/)?.groups?.at ?? {}
|
|
118
|
+
|
|
119
|
+
return at
|
|
120
|
+
? `* ${at}`
|
|
121
|
+
: line
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return lines.join("\n")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Creates an Sass from an existing Error object with additional
|
|
131
|
+
* trace message.
|
|
132
|
+
*
|
|
133
|
+
* @param {Error} error - The original error object
|
|
134
|
+
* @param {string} message - Additional trace message to add
|
|
135
|
+
* @returns {Sass} New Sass instance with trace from the original error
|
|
136
|
+
* @throws {Sass} If the first parameter is not an Error instance
|
|
137
|
+
*/
|
|
138
|
+
static from(error, message) {
|
|
139
|
+
if(!(error instanceof Error))
|
|
140
|
+
throw Sass.new("Sass.from must take an Error object.")
|
|
141
|
+
|
|
142
|
+
const oldMessage = error.message
|
|
143
|
+
const newError = new Sass(oldMessage, {cause: error}).addTrace(message)
|
|
144
|
+
|
|
145
|
+
return newError
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Factory method to create or enhance Sass instances.
|
|
150
|
+
* If error parameter is provided, enhances existing Sass or wraps
|
|
151
|
+
* other errors. Otherwise creates a new Sass instance.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} message - The error message
|
|
154
|
+
* @param {Error|Sass} [error] - Optional existing error to wrap or enhance
|
|
155
|
+
* @returns {Sass} New or enhanced Sass instance
|
|
156
|
+
*/
|
|
157
|
+
static new(message, error) {
|
|
158
|
+
if(error) {
|
|
159
|
+
return error instanceof Sass
|
|
160
|
+
? error.addTrace(message)
|
|
161
|
+
: Sass.from(error, message)
|
|
162
|
+
} else {
|
|
163
|
+
return new Sass(message)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/lib/Term.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import console from "node:console"
|
|
2
|
+
import process from "node:process"
|
|
3
|
+
|
|
4
|
+
import Sass from "./Sass.js"
|
|
5
|
+
|
|
6
|
+
export default class Term {
|
|
7
|
+
/**
|
|
8
|
+
* Log an informational message.
|
|
9
|
+
*
|
|
10
|
+
* @param {...unknown} [arg] - Values to log.
|
|
11
|
+
*/
|
|
12
|
+
static log(...arg) {
|
|
13
|
+
console.log(...arg)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Log an informational message.
|
|
18
|
+
*
|
|
19
|
+
* @param {...unknown} [arg] - Values to log.
|
|
20
|
+
*/
|
|
21
|
+
static info(...arg) {
|
|
22
|
+
console.info(...arg)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Log a warning message.
|
|
27
|
+
*
|
|
28
|
+
* @param {...unknown} [arg] - Warning text / object.
|
|
29
|
+
*/
|
|
30
|
+
static warn(...arg) {
|
|
31
|
+
console.warn(...arg)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Log an error message (plus optional details).
|
|
36
|
+
*
|
|
37
|
+
* @param {...unknown} [arg] - Values to log.
|
|
38
|
+
*/
|
|
39
|
+
static error(...arg) {
|
|
40
|
+
console.error(...arg)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Log a debug message (no-op unless console.debug provided/visible by env).
|
|
45
|
+
*
|
|
46
|
+
* @param {...unknown} [arg] - Values to log.
|
|
47
|
+
*/
|
|
48
|
+
static debug(...arg) {
|
|
49
|
+
console.debug(...arg)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Emit a status line to the terminal.
|
|
54
|
+
*
|
|
55
|
+
* Accepts either a plain string or an array of message segments (see
|
|
56
|
+
* `terminalMessage()` for formatting options). If `silent` is true, output
|
|
57
|
+
* is suppressed.
|
|
58
|
+
*
|
|
59
|
+
* This is a convenient shortcut for logging status updates, with optional
|
|
60
|
+
* formatting and easy suppression.
|
|
61
|
+
*
|
|
62
|
+
* @param {string | Array<string | [string, string]>} args - Message or segments.
|
|
63
|
+
* @param {object} [options] - Behaviour flags.
|
|
64
|
+
* @param {boolean} options.silent - When true, suppress output.
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
static status(args, {silent=false} = {}) {
|
|
68
|
+
if(silent)
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
return Term.info(Term.terminalMessage(args))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Constructs a formatted status line.
|
|
76
|
+
*
|
|
77
|
+
* Input forms:
|
|
78
|
+
* - string: printed as-is
|
|
79
|
+
* - array: each element is either:
|
|
80
|
+
* - a plain string (emitted unchanged), or
|
|
81
|
+
* - a tuple: [level, text] where `level` maps to an ansiColors alias
|
|
82
|
+
* (e.g. success, info, warn, error, modified).
|
|
83
|
+
* - a tuple: [level, text, [openBracket,closeBracket]] where `level` maps to an ansiColors alias
|
|
84
|
+
* (e.g. success, info, warn, error, modified). These are rendered as
|
|
85
|
+
* colourised bracketed segments: [TEXT].
|
|
86
|
+
*
|
|
87
|
+
* The function performs a shallow validation: tuple elements must both be
|
|
88
|
+
* strings; otherwise a TypeError is thrown. Nested arrays beyond depth 1 are
|
|
89
|
+
* not supported.
|
|
90
|
+
*
|
|
91
|
+
* Recursion: array input is normalised into a single string then re-dispatched
|
|
92
|
+
* through `status` to leverage the string branch (keeps logic DRY).
|
|
93
|
+
*
|
|
94
|
+
* @param {string | Array<string, string> | Array<string, string, string>} argList - Message spec.
|
|
95
|
+
* @returns {void}
|
|
96
|
+
*/
|
|
97
|
+
static terminalMessage(argList) {
|
|
98
|
+
if(typeof argList === "string")
|
|
99
|
+
return argList
|
|
100
|
+
|
|
101
|
+
if(Array.isArray(argList)) {
|
|
102
|
+
const message = argList
|
|
103
|
+
.map(args => {
|
|
104
|
+
// Bracketed
|
|
105
|
+
if(Array.isArray(args))
|
|
106
|
+
|
|
107
|
+
if(args.length === 3 && Array.isArray(args[2]))
|
|
108
|
+
return Term.terminalBracket(args)
|
|
109
|
+
|
|
110
|
+
else
|
|
111
|
+
return Term.terminalBracket([...args])
|
|
112
|
+
|
|
113
|
+
// Plain string, no decoration
|
|
114
|
+
if(typeof args === "string")
|
|
115
|
+
return args
|
|
116
|
+
})
|
|
117
|
+
.join(" ")
|
|
118
|
+
|
|
119
|
+
return Term.terminalMessage(message)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
throw Sass.new("Invalid arguments passed to terminalMessage")
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Construct a single coloured bracketed segment from a tuple specifying
|
|
127
|
+
* the style level and the text. The first element ("level") maps to an
|
|
128
|
+
* `ansiColors` alias (e.g. success, info, warn, error, modified) and is
|
|
129
|
+
* used both for the inner text colour and to locate its matching
|
|
130
|
+
* "-bracket" alias for the surrounding square brackets. The second
|
|
131
|
+
* element is the raw text to display.
|
|
132
|
+
*
|
|
133
|
+
* Input validation: every element of `parts` must be a string; otherwise
|
|
134
|
+
* an `Sass` error is thrown. (Additional elements beyond the first two are
|
|
135
|
+
* ignored – the method destructures only the first pair.)
|
|
136
|
+
*
|
|
137
|
+
* Example:
|
|
138
|
+
* terminalBracket(["success", "COMPILED"]) → "[COMPILED]" with coloured
|
|
139
|
+
* brackets + inner text (assuming colour support is available in the
|
|
140
|
+
* terminal).
|
|
141
|
+
*
|
|
142
|
+
* This method does not append trailing spaces; callers are responsible for
|
|
143
|
+
* joining multiple segments with appropriate separators.
|
|
144
|
+
*
|
|
145
|
+
* @param {string[]} parts - Tuple: [level, text]. Additional entries ignored.
|
|
146
|
+
* @returns {string} Colourised bracketed segment (e.g. "[TEXT]").
|
|
147
|
+
* @throws {Sass} If any element of `parts` is not a string.
|
|
148
|
+
*/
|
|
149
|
+
static terminalBracket([level, text, brackets=["[","]"]]) {
|
|
150
|
+
if(!(typeof level === "string" && typeof text === "string"))
|
|
151
|
+
throw Sass.new("Each element must be a string.")
|
|
152
|
+
|
|
153
|
+
// Simplified version without color support - just return bracketed text
|
|
154
|
+
return `${brackets[0]}${text}${brackets[1]}`
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static async resetTerminal() {
|
|
158
|
+
await Term.directWrite("\x1b[?25h")
|
|
159
|
+
process.stdin.setRawMode(false)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static async clearLines(num) {
|
|
163
|
+
await Term.directWrite(`${"\r\x1b[2K\x1b[1A".repeat(num)}`)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static directWrite(output) {
|
|
167
|
+
return new Promise(resolve => {
|
|
168
|
+
process.stdout.write(output, () => resolve())
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
}
|