@gesslar/sassy 0.21.1 → 0.21.3
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 +10 -5
- package/src/BuildCommand.js +1 -2
- package/src/Colour.js +1 -3
- package/src/Command.js +1 -4
- package/src/Compiler.js +1 -6
- package/src/Evaluator.js +1 -1
- package/src/LintCommand.js +1 -2
- package/src/ResolveCommand.js +3 -6
- package/src/Session.js +1 -4
- package/src/Theme.js +1 -7
- package/src/ThemePool.js +1 -1
- package/src/ThemeToken.js +1 -1
- package/src/cli.js +1 -5
- package/src/index.js +40 -0
- package/src/Cache.js +0 -74
- package/src/Data.js +0 -533
- package/src/DirectoryObject.js +0 -188
- package/src/File.js +0 -346
- package/src/FileObject.js +0 -226
- package/src/Sass.js +0 -166
- package/src/Term.js +0 -175
- package/src/Type.js +0 -207
- package/src/Util.js +0 -132
- package/src/Valid.js +0 -50
package/src/Sass.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Sass.js
|
|
3
|
-
*
|
|
4
|
-
* Defines the Sass class, a custom error type for theme 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 theme engine for structured error handling and
|
|
11
|
-
* debugging.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import Term from "./Term.js"
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Custom error class for Sassy theme compilation 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/Term.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import c from "@gesslar/colours"
|
|
2
|
-
// import colorSupport from "color-support"
|
|
3
|
-
import console from "node:console"
|
|
4
|
-
import process from "node:process"
|
|
5
|
-
|
|
6
|
-
import Sass from "./Sass.js"
|
|
7
|
-
|
|
8
|
-
export default class Term {
|
|
9
|
-
/**
|
|
10
|
-
* Log an informational message.
|
|
11
|
-
*
|
|
12
|
-
* @param {...unknown} [arg] - Values to log.
|
|
13
|
-
*/
|
|
14
|
-
static log(...arg) {
|
|
15
|
-
console.log(...arg)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Log an informational message.
|
|
20
|
-
*
|
|
21
|
-
* @param {...unknown} [arg] - Values to log.
|
|
22
|
-
*/
|
|
23
|
-
static info(...arg) {
|
|
24
|
-
console.info(...arg)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Log a warning message.
|
|
29
|
-
*
|
|
30
|
-
* @param {...unknown} [arg] - Warning text / object.
|
|
31
|
-
*/
|
|
32
|
-
static warn(...arg) {
|
|
33
|
-
console.warn(...arg)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Log an error message (plus optional details).
|
|
38
|
-
*
|
|
39
|
-
* @param {...unknown} [arg] - Values to log.
|
|
40
|
-
*/
|
|
41
|
-
static error(...arg) {
|
|
42
|
-
console.error(...arg)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Log a debug message (no-op unless console.debug provided/visible by env).
|
|
47
|
-
*
|
|
48
|
-
* @param {...unknown} [arg] - Values to log.
|
|
49
|
-
*/
|
|
50
|
-
static debug(...arg) {
|
|
51
|
-
console.debug(...arg)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Emit a status line to the terminal.
|
|
56
|
-
*
|
|
57
|
-
* Accepts either a plain string or an array of message segments (see
|
|
58
|
-
* `terminalMessage()` for formatting options). If `silent` is true, output
|
|
59
|
-
* is suppressed.
|
|
60
|
-
*
|
|
61
|
-
* This is a convenient shortcut for logging status updates, with optional
|
|
62
|
-
* formatting and easy suppression.
|
|
63
|
-
*
|
|
64
|
-
* @param {string | Array<string | [string, string]>} args - Message or segments.
|
|
65
|
-
* @param {object} [options] - Behaviour flags.
|
|
66
|
-
* @param {boolean} options.silent - When true, suppress output.
|
|
67
|
-
* @returns {void}
|
|
68
|
-
*/
|
|
69
|
-
static status(args, {silent=false} = {}) {
|
|
70
|
-
if(silent)
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
return Term.info(Term.terminalMessage(args))
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Constructs a formatted status line.
|
|
78
|
-
*
|
|
79
|
-
* Input forms:
|
|
80
|
-
* - string: printed as-is
|
|
81
|
-
* - array: each element is either:
|
|
82
|
-
* - a plain string (emitted unchanged), or
|
|
83
|
-
* - a tuple: [level, text] where `level` maps to an ansiColors alias
|
|
84
|
-
* (e.g. success, info, warn, error, modified).
|
|
85
|
-
* - a tuple: [level, text, [openBracket,closeBracket]] where `level` maps to an ansiColors alias
|
|
86
|
-
* (e.g. success, info, warn, error, modified). These are rendered as
|
|
87
|
-
* colourised bracketed segments: [TEXT].
|
|
88
|
-
*
|
|
89
|
-
* The function performs a shallow validation: tuple elements must both be
|
|
90
|
-
* strings; otherwise a TypeError is thrown. Nested arrays beyond depth 1 are
|
|
91
|
-
* not supported.
|
|
92
|
-
*
|
|
93
|
-
* Recursion: array input is normalised into a single string then re-dispatched
|
|
94
|
-
* through `status` to leverage the string branch (keeps logic DRY).
|
|
95
|
-
*
|
|
96
|
-
* @param {string | Array<string, string> | Array<string, string, string>} argList - Message spec.
|
|
97
|
-
* @returns {void}
|
|
98
|
-
*/
|
|
99
|
-
static terminalMessage(argList) {
|
|
100
|
-
if(typeof argList === "string")
|
|
101
|
-
return argList
|
|
102
|
-
|
|
103
|
-
if(Array.isArray(argList)) {
|
|
104
|
-
const message = argList
|
|
105
|
-
.map(args => {
|
|
106
|
-
// Bracketed
|
|
107
|
-
if(Array.isArray(args))
|
|
108
|
-
|
|
109
|
-
if(args.length === 3 && Array.isArray(args[2]))
|
|
110
|
-
return Term.terminalBracket(args)
|
|
111
|
-
|
|
112
|
-
else
|
|
113
|
-
return Term.terminalBracket([...args])
|
|
114
|
-
|
|
115
|
-
// Plain string, no decoration
|
|
116
|
-
if(typeof args === "string")
|
|
117
|
-
return args
|
|
118
|
-
})
|
|
119
|
-
.join(" ")
|
|
120
|
-
|
|
121
|
-
return Term.terminalMessage(message)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
throw Sass.new("Invalid arguments passed to terminalMessage")
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Construct a single coloured bracketed segment from a tuple specifying
|
|
129
|
-
* the style level and the text. The first element ("level") maps to an
|
|
130
|
-
* `ansiColors` alias (e.g. success, info, warn, error, modified) and is
|
|
131
|
-
* used both for the inner text colour and to locate its matching
|
|
132
|
-
* "-bracket" alias for the surrounding square brackets. The second
|
|
133
|
-
* element is the raw text to display.
|
|
134
|
-
*
|
|
135
|
-
* Input validation: every element of `parts` must be a string; otherwise
|
|
136
|
-
* an `Sass` error is thrown. (Additional elements beyond the first two are
|
|
137
|
-
* ignored – the method destructures only the first pair.)
|
|
138
|
-
*
|
|
139
|
-
* Example:
|
|
140
|
-
* terminalBracket(["success", "COMPILED"]) → "[COMPILED]" with coloured
|
|
141
|
-
* brackets + inner text (assuming colour support is available in the
|
|
142
|
-
* terminal).
|
|
143
|
-
*
|
|
144
|
-
* This method does not append trailing spaces; callers are responsible for
|
|
145
|
-
* joining multiple segments with appropriate separators.
|
|
146
|
-
*
|
|
147
|
-
* @param {string[]} parts - Tuple: [level, text]. Additional entries ignored.
|
|
148
|
-
* @returns {string} Colourised bracketed segment (e.g. "[TEXT]").
|
|
149
|
-
* @throws {Sass} If any element of `parts` is not a string.
|
|
150
|
-
*/
|
|
151
|
-
static terminalBracket([level, text, brackets=["",""]]) {
|
|
152
|
-
if(!(typeof level === "string" && typeof text === "string"))
|
|
153
|
-
throw Sass.new("Each element must be a string.")
|
|
154
|
-
|
|
155
|
-
return "" +
|
|
156
|
-
c`{${level}-bracket}${brackets[0]}{/}`
|
|
157
|
-
+ c`{${level}}${text}{/}`
|
|
158
|
-
+ c`{${level}-bracket}${brackets[1]}{/}`
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
static async resetTerminal() {
|
|
162
|
-
await Term.directWrite("\x1b[?25h")
|
|
163
|
-
process.stdin.setRawMode(false)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
static async clearLines(num) {
|
|
167
|
-
await Term.directWrite(`${"\r\x1b[2K\x1b[1A".repeat(num)}`)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
static directWrite(output) {
|
|
171
|
-
return new Promise(resolve => {
|
|
172
|
-
process.stdout.write(output, () => resolve())
|
|
173
|
-
})
|
|
174
|
-
}
|
|
175
|
-
}
|
package/src/Type.js
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Type specification and validation utilities.
|
|
3
|
-
* Provides TypeSpec class for parsing and validating complex type specifications
|
|
4
|
-
* including arrays, unions, and options.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import Sass from "./Sass.js"
|
|
8
|
-
import Data from "./Data.js"
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Type specification class for parsing and validating complex type definitions.
|
|
12
|
-
* Supports union types, array types, and validation options.
|
|
13
|
-
*/
|
|
14
|
-
export default class TypeSpec {
|
|
15
|
-
#specs
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Creates a new TypeSpec instance.
|
|
19
|
-
*
|
|
20
|
-
* @param {string} string - The type specification string (e.g., "string|number", "object[]")
|
|
21
|
-
* @param {object} options - Additional parsing options
|
|
22
|
-
*/
|
|
23
|
-
constructor(string, options) {
|
|
24
|
-
this.#specs = []
|
|
25
|
-
this.#parse(string, options)
|
|
26
|
-
Object.freeze(this.#specs)
|
|
27
|
-
this.specs = this.#specs
|
|
28
|
-
this.length = this.#specs.length
|
|
29
|
-
this.stringRepresentation = this.toString()
|
|
30
|
-
Object.freeze(this)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Returns a string representation of the type specification.
|
|
35
|
-
*
|
|
36
|
-
* @returns {string} The type specification as a string (e.g., "string|number[]")
|
|
37
|
-
*/
|
|
38
|
-
toString() {
|
|
39
|
-
return this.#specs
|
|
40
|
-
.map(spec => {
|
|
41
|
-
return `${spec.typeName}${spec.array ? "[]" : ""}`
|
|
42
|
-
})
|
|
43
|
-
.join("|")
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Returns a JSON representation of the TypeSpec.
|
|
48
|
-
*
|
|
49
|
-
* @returns {object} Object containing specs, length, and string representation
|
|
50
|
-
*/
|
|
51
|
-
toJSON() {
|
|
52
|
-
// Serialize as a string representation or as raw data
|
|
53
|
-
return {
|
|
54
|
-
specs: this.#specs,
|
|
55
|
-
length: this.length,
|
|
56
|
-
stringRepresentation: this.toString(),
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Executes a provided function once for each type specification.
|
|
62
|
-
*
|
|
63
|
-
* @param {function(unknown): void} callback - Function to execute for each spec
|
|
64
|
-
*/
|
|
65
|
-
forEach(callback) {
|
|
66
|
-
this.#specs.forEach(callback)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Tests whether all type specifications pass the provided test function.
|
|
71
|
-
*
|
|
72
|
-
* @param {function(unknown): boolean} callback - Function to test each spec
|
|
73
|
-
* @returns {boolean} True if all specs pass the test
|
|
74
|
-
*/
|
|
75
|
-
every(callback) {
|
|
76
|
-
return this.#specs.every(callback)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Tests whether at least one type specification passes the provided test function.
|
|
81
|
-
*
|
|
82
|
-
* @param {function(unknown): boolean} callback - Function to test each spec
|
|
83
|
-
* @returns {boolean} True if at least one spec passes the test
|
|
84
|
-
*/
|
|
85
|
-
some(callback) {
|
|
86
|
-
return this.#specs.some(callback)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Creates a new array with all type specifications that pass the provided test function.
|
|
91
|
-
*
|
|
92
|
-
* @param {function(unknown): boolean} callback - Function to test each spec
|
|
93
|
-
* @returns {Array} New array with filtered specs
|
|
94
|
-
*/
|
|
95
|
-
filter(callback) {
|
|
96
|
-
return this.#specs.filter(callback)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Creates a new array populated with the results of calling the provided function on every spec.
|
|
101
|
-
*
|
|
102
|
-
* @param {function(unknown): unknown} callback - Function to call on each spec
|
|
103
|
-
* @returns {Array} New array with mapped values
|
|
104
|
-
*/
|
|
105
|
-
map(callback) {
|
|
106
|
-
return this.#specs.map(callback)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Executes a reducer function on each spec, resulting in a single output value.
|
|
111
|
-
*
|
|
112
|
-
* @param {function(unknown, unknown): unknown} callback - Function to execute on each spec
|
|
113
|
-
* @param {unknown} initialValue - Initial value for the accumulator
|
|
114
|
-
* @returns {unknown} The final accumulated value
|
|
115
|
-
*/
|
|
116
|
-
reduce(callback, initialValue) {
|
|
117
|
-
return this.#specs.reduce(callback, initialValue)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Returns the first type specification that satisfies the provided testing function.
|
|
122
|
-
*
|
|
123
|
-
* @param {function(unknown): boolean} callback - Function to test each spec
|
|
124
|
-
* @returns {object|undefined} The first spec that matches, or undefined
|
|
125
|
-
*/
|
|
126
|
-
find(callback) {
|
|
127
|
-
return this.#specs.find(callback)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Tests whether a value matches any of the type specifications.
|
|
132
|
-
* Handles array types, union types, and empty value validation.
|
|
133
|
-
*
|
|
134
|
-
* @param {unknown} value - The value to test against the type specifications
|
|
135
|
-
* @param {object} options - Validation options
|
|
136
|
-
* @param {boolean} options.allowEmpty - Whether empty values are allowed
|
|
137
|
-
* @returns {boolean} True if the value matches any type specification
|
|
138
|
-
*/
|
|
139
|
-
match(value, options) {
|
|
140
|
-
const allowEmpty = options?.allowEmpty ?? true
|
|
141
|
-
const empty = Data.isEmpty(value)
|
|
142
|
-
|
|
143
|
-
// If we have a list of types, because the string was validly parsed,
|
|
144
|
-
// we need to ensure that all of the types that were parsed are valid types
|
|
145
|
-
// in JavaScript.
|
|
146
|
-
if(this.length && !this.every(t => Data.isValidType(t.typeName)))
|
|
147
|
-
return false
|
|
148
|
-
|
|
149
|
-
// Now, let's do some checking with the types, respecting the array flag
|
|
150
|
-
// with the value
|
|
151
|
-
const valueType = Data.typeOf(value)
|
|
152
|
-
const isArray = valueType === "array"
|
|
153
|
-
|
|
154
|
-
// We need to ensure that we match the type and the consistency of the types
|
|
155
|
-
// in an array, if it is an array and an array is allowed.
|
|
156
|
-
const matchingTypeSpec = this.filter(spec => {
|
|
157
|
-
const {typeName: allowedType, array: allowedArray} = spec
|
|
158
|
-
|
|
159
|
-
if(valueType === allowedType && !isArray && !allowedArray)
|
|
160
|
-
return !allowEmpty ? !empty : true
|
|
161
|
-
|
|
162
|
-
if(isArray) {
|
|
163
|
-
if(allowedType === "array")
|
|
164
|
-
if(!allowedArray)
|
|
165
|
-
return true
|
|
166
|
-
|
|
167
|
-
if(empty)
|
|
168
|
-
if(allowEmpty)
|
|
169
|
-
return true
|
|
170
|
-
|
|
171
|
-
return Data.isArrayUniform(value, allowedType)
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
return matchingTypeSpec.length > 0
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Parses a type specification string into individual type specs.
|
|
180
|
-
* Handles union types separated by delimiters and array notation.
|
|
181
|
-
*
|
|
182
|
-
* @private
|
|
183
|
-
* @param {string} string - The type specification string to parse
|
|
184
|
-
* @param {object} options - Parsing options
|
|
185
|
-
* @param {string} options.delimiter - The delimiter for union types
|
|
186
|
-
* @throws {TypeError} If the type specification is invalid
|
|
187
|
-
*/
|
|
188
|
-
#parse(string, options) {
|
|
189
|
-
const delimiter = options?.delimiter ?? "|"
|
|
190
|
-
const parts = string.split(delimiter)
|
|
191
|
-
|
|
192
|
-
this.#specs = parts.map(part => {
|
|
193
|
-
const typeMatches = /(\w+)(\[\])?/.exec(part)
|
|
194
|
-
|
|
195
|
-
if(!typeMatches || typeMatches.length !== 3)
|
|
196
|
-
throw Sass.new(`Invalid type: ${part}`)
|
|
197
|
-
|
|
198
|
-
if(!Data.isValidType(typeMatches[1]))
|
|
199
|
-
throw Sass.new(`Invalid type: ${typeMatches[1]}`)
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
typeName: typeMatches[1],
|
|
203
|
-
array: typeMatches[2] === "[]",
|
|
204
|
-
}
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
}
|
package/src/Util.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import {createHash} from "node:crypto"
|
|
2
|
-
import {performance} from "node:perf_hooks"
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Utility class providing common helper functions for string manipulation,
|
|
6
|
-
* timing, hashing, and option parsing.
|
|
7
|
-
*/
|
|
8
|
-
export default class Util {
|
|
9
|
-
/**
|
|
10
|
-
* Capitalizes the first letter of a string.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} text - The text to capitalize
|
|
13
|
-
* @returns {string} Text with first letter capitalized
|
|
14
|
-
*/
|
|
15
|
-
static capitalize(text) {
|
|
16
|
-
return `${text.slice(0,1).toUpperCase()}${text.slice(1)}`
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Measure wall-clock time for an async function.
|
|
21
|
-
*
|
|
22
|
-
* @template T
|
|
23
|
-
* @param {() => Promise<T>} fn - Thunk returning a promise.
|
|
24
|
-
* @returns {Promise<{result: T, cost: number}>} Object containing result and elapsed ms (number, 1 decimal).
|
|
25
|
-
*/
|
|
26
|
-
static async time(fn) {
|
|
27
|
-
const t0 = performance.now()
|
|
28
|
-
const result = await fn()
|
|
29
|
-
const cost = Math.round((performance.now() - t0) * 10) / 10
|
|
30
|
-
|
|
31
|
-
return {result, cost}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Right-align a string inside a fixed width (left pad with spaces).
|
|
36
|
-
* If the string exceeds width it is returned unchanged.
|
|
37
|
-
*
|
|
38
|
-
* @param {string|number} text - Text to align.
|
|
39
|
-
* @param {number} width - Target field width (default 80).
|
|
40
|
-
* @returns {string} Padded string.
|
|
41
|
-
*/
|
|
42
|
-
static rightAlignText(text, width=80) {
|
|
43
|
-
const work = String(text)
|
|
44
|
-
|
|
45
|
-
if(work.length > width)
|
|
46
|
-
return work
|
|
47
|
-
|
|
48
|
-
const diff = width-work.length
|
|
49
|
-
|
|
50
|
-
return `${" ".repeat(diff)}${work}`
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Compute sha256 hash (hex) of the provided string.
|
|
55
|
-
*
|
|
56
|
-
* @param {string} s - Input string.
|
|
57
|
-
* @returns {string} 64-char hexadecimal digest.
|
|
58
|
-
*/
|
|
59
|
-
static hashOf(s) {
|
|
60
|
-
return createHash("sha256").update(s).digest("hex")
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Extracts canonical option names from a Commander-style options object.
|
|
65
|
-
*
|
|
66
|
-
* Each key in the input object is a string containing one or more option
|
|
67
|
-
* forms, separated by commas (e.g. "-w, --watch"). This function splits each
|
|
68
|
-
* key, trims whitespace, and parses out the long option name (e.g. "watch")
|
|
69
|
-
* for each entry. If no long option ("--") is present, the short option (e.g.
|
|
70
|
-
* "v" from "-v") will be included in the result array. If both are present,
|
|
71
|
-
* the long option is preferred.
|
|
72
|
-
*
|
|
73
|
-
* Example:
|
|
74
|
-
* generateOptionNames({"-w, --watch": "desc", "-v": "desc"})
|
|
75
|
-
* → ["watch", "v"]
|
|
76
|
-
*
|
|
77
|
-
* Edge cases:
|
|
78
|
-
* - If a key contains only a short option ("-v"), that short name will be
|
|
79
|
-
* included in the result.
|
|
80
|
-
* - If multiple long options are present, only the first is used.
|
|
81
|
-
* - If the option string is malformed, may return undefined for that entry
|
|
82
|
-
* (filtered out).
|
|
83
|
-
*
|
|
84
|
-
* @param {object} object - Mapping of option strings to descriptions.
|
|
85
|
-
* @returns {string[]} Array of canonical option names (long preferred, short if no long present).
|
|
86
|
-
*/
|
|
87
|
-
static generateOptionNames(object) {
|
|
88
|
-
return Object.keys(object)
|
|
89
|
-
.map(key => {
|
|
90
|
-
return key
|
|
91
|
-
.split(",")
|
|
92
|
-
.map(o => o.trim())
|
|
93
|
-
.map(o => o.match(/^(?<sign>--?)(?<option>[\w-]+)/).groups)
|
|
94
|
-
.reduce((acc, curr) => acc.sign === "--" ? acc : curr, {})
|
|
95
|
-
?.option
|
|
96
|
-
})
|
|
97
|
-
.filter(Boolean)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Asynchronously awaits all promises in parallel.
|
|
102
|
-
* Wrapper around Promise.all for consistency with other utility methods.
|
|
103
|
-
*
|
|
104
|
-
* @param {Promise[]} promises - Array of promises to await
|
|
105
|
-
* @returns {Promise<unknown[]>} Results of all promises
|
|
106
|
-
*/
|
|
107
|
-
static async awaitAll(promises) {
|
|
108
|
-
return await Promise.all(promises)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Settles all promises (both fulfilled and rejected) in parallel.
|
|
113
|
-
* Wrapper around Promise.allSettled for consistency with other utility methods.
|
|
114
|
-
*
|
|
115
|
-
* @param {Promise[]} promises - Array of promises to settle
|
|
116
|
-
* @returns {Promise<Array>} Results of all settled promises with status and value/reason
|
|
117
|
-
*/
|
|
118
|
-
static async settleAll(promises) {
|
|
119
|
-
return await Promise.allSettled(promises)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Returns the first promise to resolve or reject from an array of promises.
|
|
124
|
-
* Wrapper around Promise.race for consistency with other utility methods.
|
|
125
|
-
*
|
|
126
|
-
* @param {Promise[]} promises - Array of promises to race
|
|
127
|
-
* @returns {Promise<unknown>} Result of the first settled promise
|
|
128
|
-
*/
|
|
129
|
-
static async race(promises) {
|
|
130
|
-
return await Promise.race(promises)
|
|
131
|
-
}
|
|
132
|
-
}
|