@gesslar/sassy 0.20.2 → 0.21.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.
@@ -10,7 +10,6 @@ import Theme from "./Theme.js"
10
10
  import Util from "./Util.js"
11
11
  import Data from "./Data.js"
12
12
 
13
-
14
13
  // ansiColors.enabled = colorSupport.hasBasic
15
14
 
16
15
  /**
@@ -26,12 +25,12 @@ export default class ResolveCommand extends Command {
26
25
  constructor(base) {
27
26
  super(base)
28
27
 
29
- this.cliCommand = "resolve <file>"
30
- this.cliOptions = {
28
+ this.setCliCommand("resolve <file>")
29
+ this.setCliOptions({
31
30
  "color": ["-c, --color <key>", "resolve a color key to its final evaluated value"],
32
31
  "tokenColor": ["-t, --tokenColor <scope>", "resolve a tokenColors scope to its final evaluated value"],
33
32
  "semanticTokenColor": ["-s, --semanticTokenColor <scope>", "resolve a semanticTokenColors scope to its final evaluated value"],
34
- }
33
+ })
35
34
  }
36
35
 
37
36
  /**
@@ -43,22 +42,23 @@ export default class ResolveCommand extends Command {
43
42
  * @returns {Promise<void>} Resolves when resolution is complete
44
43
  */
45
44
  async execute(inputArg, options={}) {
45
+ const cliOptionNames = this.getCliOptionNames()
46
46
  const intersection =
47
- Data.arrayIntersection(this.cliOptionNames, Object.keys(options))
47
+ Data.arrayIntersection(cliOptionNames, Object.keys(options))
48
48
 
49
49
  if(intersection.length > 1)
50
50
  throw Sass.new(
51
- `The options ${this.cliOptionNames.join(", ")} are ` +
51
+ `The options ${cliOptionNames.join(", ")} are ` +
52
52
  `mutually exclusive and may only have one expressed in the request.`
53
53
  )
54
54
 
55
- const {cwd} = this
55
+ const cwd = this.getCwd()
56
56
  const optionName = Object.keys(options??{})
57
- .find(o => this.cliOptionNames.includes(o))
57
+ .find(o => cliOptionNames.includes(o))
58
58
 
59
59
  if(!optionName) {
60
60
  throw Sass.new(
61
- `No valid option provided. Please specify one of: ${this.cliOptionNames.join(", ")}.`
61
+ `No valid option provided. Please specify one of: ${cliOptionNames.join(", ")}.`
62
62
  )
63
63
  }
64
64
 
@@ -71,7 +71,8 @@ export default class ResolveCommand extends Command {
71
71
 
72
72
  const fileObject = await this.resolveThemeFileName(inputArg, cwd)
73
73
  const theme = new Theme(fileObject, cwd, options)
74
- theme.cache = this.cache
74
+
75
+ theme.setCache(this.getCache())
75
76
 
76
77
  await theme.load()
77
78
  await theme.build()
@@ -86,13 +87,22 @@ export default class ResolveCommand extends Command {
86
87
  * @param {object} theme - The compiled theme object with pool
87
88
  * @param {string} colorName - The color key to resolve
88
89
  * @returns {void}
90
+ * @example
91
+ * // Resolve a color variable from a compiled theme
92
+ * await resolveCommand.resolveColor(theme, 'colors.primary');
93
+ * // Output:
94
+ * // colors.primary:
95
+ * // $(vars.accent)
96
+ * // → #3366cc
97
+ * // Resolution: #3366cc
89
98
  */
90
99
  async resolveColor(theme, colorName) {
91
- const pool = theme.pool
100
+ const pool = theme.getPool()
101
+
92
102
  if(!pool || !pool.has(colorName))
93
103
  return Term.info(`'${colorName}' not found.`)
94
104
 
95
- const tokens = pool.getTokens
105
+ const tokens = pool.getTokens()
96
106
  const token = tokens.get(colorName)
97
107
  const trail = token.getTrail()
98
108
  const fullTrail = this.#buildCompleteTrail(token, trail)
@@ -114,7 +124,7 @@ export default class ResolveCommand extends Command {
114
124
  * @returns {void}
115
125
  */
116
126
  async resolveTokenColor(theme, scopeName) {
117
- const tokenColors = theme.output?.tokenColors || []
127
+ const tokenColors = theme.getOutput()?.tokenColors || []
118
128
 
119
129
  // Check if this is a disambiguated scope (ends with .1, .2, etc.)
120
130
  const disambiguatedMatch = scopeName.match(/^(.+)\.(\d+)$/)
@@ -127,7 +137,9 @@ export default class ResolveCommand extends Command {
127
137
 
128
138
  if(index >= 0 && index < matches.length) {
129
139
  const match = matches[index]
140
+
130
141
  await this.#resolveScopeMatch(theme, match, `${baseScope}.${indexStr}`)
142
+
131
143
  return
132
144
  } else {
133
145
  return Term.info(`'${scopeName}' not found. Available: ${baseScope}.1 through ${baseScope}.${matches.length}`)
@@ -149,6 +161,7 @@ export default class ResolveCommand extends Command {
149
161
  Term.info(`Multiple entries found for '${scopeName}', please try again with the specific query:\n`)
150
162
  matches.forEach((match, index) => {
151
163
  const name = match.name || `Entry ${index + 1}`
164
+
152
165
  Term.info(`${name}: ${scopeName}.${index + 1}`)
153
166
  })
154
167
  }
@@ -161,24 +174,26 @@ export default class ResolveCommand extends Command {
161
174
 
162
175
  // Handle comma-separated scopes
163
176
  const scopes = entry.scope.split(",").map(s => s.trim())
177
+
164
178
  return scopes.includes(targetScope)
165
179
  })
166
180
  }
167
181
 
168
182
  async #resolveScopeMatch(theme, match, displayName) {
169
- const pool = theme.pool
183
+ const pool = theme.getPool()
170
184
  const settings = match.settings || {}
171
185
  const name = match.name || "Unnamed"
172
186
 
173
187
  // Look for the foreground property specifically
174
188
  const foreground = settings.foreground
189
+
175
190
  if(!foreground) {
176
191
  return Term.info(`${displayName} (${name})\n\n(no foreground property)`)
177
192
  }
178
193
 
179
194
  // First, try to find the token by looking for variables that resolve to this value
180
195
  // but prioritize source variable names over computed results
181
- const tokens = pool ? pool.getTokens : new Map()
196
+ const tokens = pool ? pool.getTokens() : new Map()
182
197
  let bestToken = null
183
198
 
184
199
  // First try to find a scope.* token that matches
@@ -205,16 +220,18 @@ export default class ResolveCommand extends Command {
205
220
  }
206
221
  }
207
222
 
208
- if(!bestToken) {
209
- return Term.info(`${displayName} (${name})\n\n(resolved to static value: ${foreground})`)
210
- }
223
+ if(!bestToken)
224
+ return Term.info(
225
+ `${displayName} (${name})\n\n(resolved to static value: ${foreground})`
226
+ )
211
227
 
212
228
  const trail = bestToken.getTrail()
213
229
  const fullTrail = this.#buildCompleteTrail(bestToken, trail)
214
230
  const finalValue = bestToken.getValue()
215
231
  const [formattedFinalValue] = this.#formatLeaf(finalValue)
216
-
217
- const output = c`{head}${displayName}{/} {hex}${(`${name}`)}{/}\n${this.#formatOutput(fullTrail)}\n\n{head}${"Resolution:"}{/} ${formattedFinalValue}`
232
+ const output = c`{head}${displayName}{/} {hex}${(`${name}`)}{/}\n`+
233
+ `${this.#formatOutput(fullTrail)}\n\n{head}`+
234
+ `${"Resolution:"}{/} ${formattedFinalValue}`
218
235
 
219
236
  Term.info(output)
220
237
  }
@@ -230,18 +247,20 @@ export default class ResolveCommand extends Command {
230
247
  async resolveSemanticTokenColor(theme, scopeName) {
231
248
  // semanticTokenColors has the same structure as tokenColors, so we can reuse the logic
232
249
  // but we need to look at the semanticTokenColors array instead
233
- const originalTokenColors = theme.output?.tokenColors
250
+ const originalTokenColors = theme.getOutput()?.tokenColors
234
251
 
235
252
  // Temporarily replace tokenColors with semanticTokenColors for resolution
236
- if(theme.output?.semanticTokenColors) {
237
- theme.output.tokenColors = theme.output.semanticTokenColors
253
+ const themeOutput = theme.getOutput()
254
+
255
+ if(themeOutput?.semanticTokenColors) {
256
+ themeOutput.tokenColors = themeOutput.semanticTokenColors
238
257
  }
239
258
 
240
259
  await this.resolveTokenColor(theme, scopeName)
241
260
 
242
261
  // Restore original tokenColors
243
- if(originalTokenColors) {
244
- theme.output.tokenColors = originalTokenColors
262
+ if(originalTokenColors && themeOutput) {
263
+ themeOutput.tokenColors = originalTokenColors
245
264
  }
246
265
  }
247
266
 
@@ -251,6 +270,7 @@ export default class ResolveCommand extends Command {
251
270
 
252
271
  // Add the root token's original expression
253
272
  const rootRaw = rootToken.getRawValue()
273
+
254
274
  if(rootRaw !== rootToken.getName()) {
255
275
  steps.push({
256
276
  value: rootRaw,
@@ -265,6 +285,7 @@ export default class ResolveCommand extends Command {
265
285
  return
266
286
 
267
287
  const id = `${token.getName()}-${token.getRawValue()}`
288
+
268
289
  if(seen.has(id))
269
290
  return
270
291
 
@@ -290,7 +311,8 @@ export default class ResolveCommand extends Command {
290
311
  const depFinal = dependency.getValue()
291
312
 
292
313
  // Add dependency's expression if it's a function call
293
- if(depRaw !== dependency.getName() && !steps.some(s => s.value === depRaw)) {
314
+ if(depRaw !== dependency.getName() &&
315
+ !steps.some(s => s.value === depRaw)) {
294
316
  steps.push({
295
317
  value: depRaw,
296
318
  type: "expression",
@@ -300,6 +322,7 @@ export default class ResolveCommand extends Command {
300
322
 
301
323
  // Process dependency's trail
302
324
  const depTrail = dependency.getTrail()
325
+
303
326
  if(depTrail && depTrail.length > 0) {
304
327
  depTrail.forEach(depToken => processToken(depToken, level + 1))
305
328
  }
@@ -372,10 +395,12 @@ export default class ResolveCommand extends Command {
372
395
  // Hex results are indented one extra level with just spaces and arrow
373
396
  const prefix = " ".repeat(depth + 1)
374
397
  const arrow = c`{arrow}→{/} `
398
+
375
399
  out.push(`${prefix}${arrow}${line}`)
376
400
  } else {
377
401
  // Everything else just gets clean indentation
378
402
  const prefix = " ".repeat(depth)
403
+
379
404
  out.push(`${prefix}${line}`)
380
405
  }
381
406
  })
@@ -410,18 +435,24 @@ export default class ResolveCommand extends Command {
410
435
  }
411
436
 
412
437
  if(this.#func.test(value)) {
413
- const {func,args} = this.#func.exec(value).groups
438
+ const result = Evaluator.extractFunctionCall(value)
439
+
440
+ if(!result)
441
+ return [c`{leaf}${value}{/}`, "literal"]
442
+
443
+ const {func, args} = result
444
+
414
445
  return [
415
446
  c`{func}${func}{/}{parens}${"("}{/}{leaf}${args}{/}{parens}${")"}{/}`,
416
447
  "function"
417
448
  ]
418
449
  }
419
450
 
420
-
421
451
  if(this.#sub.test(value)) {
452
+ const varValue = Evaluator.extractVariableName(value) || value
422
453
  const {parens,none,braces} = Evaluator.sub.exec(value)?.groups || {}
423
454
  const style = (braces && ["{","}"]) || (parens && ["(",")"]) || (none && ["",""])
424
- const varValue = braces || parens || none || value
455
+
425
456
  return [
426
457
  c`{func}{/}{parens}${style[0]}{/}{leaf}${varValue}{/}{parens}${style[1]}{/}`,
427
458
  "variable"
package/src/Sass.js CHANGED
@@ -24,7 +24,7 @@ export default class Sass extends Error {
24
24
  * Creates a new Sass instance.
25
25
  *
26
26
  * @param {string} message - The error message
27
- * @param {...any} arg - Additional arguments passed to parent Error constructor
27
+ * @param {...unknown} [arg] - Additional arguments passed to parent Error constructor
28
28
  */
29
29
  constructor(message, ...arg) {
30
30
  super(message, ...arg)
@@ -115,6 +115,7 @@ export default class Sass extends Error {
115
115
  .split("\n")
116
116
  .map(line => {
117
117
  const at = line.match(/^\s{4}at\s(?<at>.*)$/)?.groups?.at ?? {}
118
+
118
119
  return at
119
120
  ? `* ${at}`
120
121
  : line
package/src/Session.js CHANGED
@@ -7,19 +7,156 @@ import Term from "./Term.js"
7
7
  import Theme from "./Theme.js"
8
8
  import Util from "./Util.js"
9
9
 
10
+ /**
11
+ * @typedef {object} SessionOptions
12
+ * @property {boolean} [watch] - Whether to enable file watching
13
+ * @property {boolean} [nerd] - Whether to show verbose output
14
+ * @property {boolean} [dryRun] - Whether to skip file writes
15
+ */
16
+
17
+ /**
18
+ * @typedef {object} BuildRecord
19
+ * @property {number} timestamp - Epoch ms when the build started
20
+ * @property {number} loadTime - Time (ms) spent loading theme sources
21
+ * @property {number} buildTime - Time (ms) spent compiling the theme
22
+ * @property {number} writeTime - Time (ms) spent writing the output file
23
+ * @property {boolean} success - Whether the build completed successfully
24
+ * @property {string} [error] - Error message when success is false
25
+ */
26
+
10
27
  export default class Session {
28
+ /**
29
+ * The theme instance managed by this session.
30
+ *
31
+ * @type {Theme|null}
32
+ * @private
33
+ */
11
34
  #theme = null
35
+
36
+ /**
37
+ * The parent command orchestrating this session.
38
+ *
39
+ * @type {Command|null}
40
+ * @private
41
+ */
12
42
  #command = null
43
+
44
+ /**
45
+ * Build configuration options for this session.
46
+ *
47
+ * @type {SessionOptions|null}
48
+ * @private
49
+ */
13
50
  #options = null
51
+
52
+ /**
53
+ * Active file system watcher for theme dependencies.
54
+ *
55
+ * @type {import("chokidar").FSWatcher|null}
56
+ * @private
57
+ */
14
58
  #watcher = null
59
+
60
+ /**
61
+ * Historical records of builds executed during this session.
62
+ *
63
+ * @type {Array<BuildRecord>}
64
+ * @private
65
+ */
15
66
  #history = []
67
+
68
+ /**
69
+ * Cumulative build statistics for this session.
70
+ *
71
+ * @type {{builds: number, failures: number}}
72
+ * @private
73
+ */
16
74
  #stats = Object.seal({builds: 0, failures: 0})
75
+
76
+ /**
77
+ * Flag indicating whether a build is currently in progress.
78
+ *
79
+ * @type {boolean}
80
+ * @private
81
+ */
17
82
  #building = false
18
83
 
19
84
  get theme() {
20
85
  return this.#theme
21
86
  }
22
87
 
88
+ /**
89
+ * Gets the theme instance managed by this session.
90
+ *
91
+ * @returns {Theme} The theme instance
92
+ */
93
+ getTheme() {
94
+ return this.#theme
95
+ }
96
+
97
+ /**
98
+ * Gets the command instance orchestrating this session.
99
+ *
100
+ * @returns {Command} The command instance
101
+ */
102
+ getCommand() {
103
+ return this.#command
104
+ }
105
+
106
+ /**
107
+ * Gets the session options.
108
+ *
109
+ * @returns {SessionOptions} The session options
110
+ */
111
+ getOptions() {
112
+ return this.#options
113
+ }
114
+
115
+ /**
116
+ * Gets the build history for this session.
117
+ *
118
+ * @returns {Array<BuildRecord>} Array of build records
119
+ */
120
+ getHistory() {
121
+ return this.#history
122
+ }
123
+
124
+ /**
125
+ * Gets the build statistics for this session.
126
+ *
127
+ * @returns {{builds: number, failures: number}} Build statistics
128
+ */
129
+ getStats() {
130
+ return this.#stats
131
+ }
132
+
133
+ /**
134
+ * Checks if a build is currently in progress.
135
+ *
136
+ * @returns {boolean} True if building
137
+ */
138
+ isBuilding() {
139
+ return this.#building
140
+ }
141
+
142
+ /**
143
+ * Checks if watch mode is enabled.
144
+ *
145
+ * @returns {boolean} True if watching
146
+ */
147
+ isWatching() {
148
+ return this.#options?.watch === true
149
+ }
150
+
151
+ /**
152
+ * Checks if there's an active file watcher.
153
+ *
154
+ * @returns {boolean} True if watcher exists
155
+ */
156
+ hasWatcher() {
157
+ return this.#watcher !== null
158
+ }
159
+
23
160
  /**
24
161
  * Creates a new Session instance for managing theme compilation lifecycle.
25
162
  * Sessions provide persistent state across rebuilds, error tracking, and
@@ -27,10 +164,7 @@ export default class Session {
27
164
  *
28
165
  * @param {Command} command - The parent build command instance
29
166
  * @param {Theme} theme - The theme instance to manage
30
- * @param {object} options - Build configuration options
31
- * @param {boolean} [options.watch] - Whether to enable file watching
32
- * @param {boolean} [options.nerd] - Whether to show verbose output
33
- * @param {boolean} [options.dryRun] - Whether to skip file writes
167
+ * @param {SessionOptions} options - Build configuration options
34
168
  */
35
169
  constructor(command, theme, options) {
36
170
  this.#command = command
@@ -88,11 +222,12 @@ export default class Session {
88
222
  */
89
223
 
90
224
  loadCost = (await Util.time(() => this.#theme.load())).cost
91
- const bytes = await File.fileSize(this.#theme.sourceFile)
225
+ const bytes = await File.fileSize(this.#theme.getSourceFile())
226
+
92
227
  Term.status([
93
228
  ["success", Util.rightAlignText(`${loadCost.toLocaleString()}ms`, 10), ["[","]"]],
94
- `${this.#theme.name} loaded`,
95
- ["info", `${bytes} bytes`, ["[","]"]]
229
+ `${this.#theme.getName()} loaded`,
230
+ ["info", `${bytes.toLocaleString()} bytes`, ["[","]"]]
96
231
  ], this.#options)
97
232
  /**
98
233
  * ****************************************************************
@@ -101,30 +236,48 @@ export default class Session {
101
236
  */
102
237
 
103
238
  buildCost = (await Util.time(() => this.#theme.build())).cost
104
-
105
- const compileResult =
106
- await Promise.allSettled(this.#theme.dependencies.map(async dep => {
107
-
108
- return await (async fileObject => {
109
- const fileName = File.relativeOrAbsolutePath(this.#command.cwd, fileObject)
110
- const fileSize = await File.fileSize(fileObject)
111
- return [fileName, fileSize]
112
- })(dep)
113
-
239
+ const dependencyFiles = Array
240
+ .from(this.#theme.getDependencies())
241
+ .map(d => d.getSourceFile())
242
+ .filter(f => f != null) // Filter out any null/undefined files
243
+
244
+ const compileResult = await Promise
245
+ .allSettled(dependencyFiles.map(async fileObject => {
246
+ if(!fileObject) {
247
+ throw new Error("Invalid dependency file object")
248
+ }
249
+
250
+ const fileName = File.relativeOrAbsolutePath(
251
+ this.#command.getCwd(), fileObject
252
+ )
253
+ const fileSize = await File.fileSize(fileObject)
254
+
255
+ return [fileName, fileSize]
114
256
  }))
115
257
 
116
258
  const rejected = compileResult.filter(result => result.status === "rejected")
259
+
117
260
  if(rejected.length > 0) {
118
261
  rejected.forEach(reject => Term.error(reject.reason))
119
262
  throw new Error("Compilation failed")
120
263
  }
121
264
 
122
- const dependencies = compileResult.slice(1).map(dep => dep.value)
123
- const totalBytes = compileResult.reduce((acc,curr) => acc + curr.value[1], 0)
265
+ const dependencies = compileResult
266
+ .slice(1)
267
+ .map(dep => dep?.value)
268
+ .filter(Boolean)
269
+
270
+ const totalBytes = compileResult.reduce(
271
+ (acc,curr) => acc + (curr?.value[1] ?? 0), 0
272
+ )
124
273
 
125
274
  Term.status([
126
- ["success", Util.rightAlignText(`${buildCost.toLocaleString()}ms`, 10), ["[","]"]],
127
- `${this.#theme.name} compiled`,
275
+ [
276
+ "success",
277
+ Util.rightAlignText(`${buildCost.toLocaleString()}ms`, 10),
278
+ ["[","]"]
279
+ ],
280
+ `${this.#theme.getName()} compiled`,
128
281
  ["success", `${compileResult[0].value[1].toLocaleString()} bytes`, ["[","]"]],
129
282
  ["info", `${totalBytes.toLocaleString()} total bytes`, ["(",")"]],
130
283
  ], this.#options)
@@ -150,19 +303,25 @@ export default class Session {
150
303
  */
151
304
 
152
305
  const writeResult = await Util.time(() => this.#theme.write(forceWrite))
306
+
153
307
  writeCost = writeResult.cost
154
- const result = writeResult.result
155
308
  const {
156
309
  status: writeStatus,
157
310
  file: outputFile,
158
311
  bytes: writeBytes
159
- } = result
160
- const outputFilename = File.relativeOrAbsolutePath(this.#command.cwd, outputFile)
312
+ } = writeResult.result
313
+ const outputFilename = File.relativeOrAbsolutePath(
314
+ this.#command.getCwd(), outputFile
315
+ )
161
316
  const status = [
162
- ["success", Util.rightAlignText(`${writeCost.toLocaleString()}ms`, 10), ["[","]"]],
317
+ [
318
+ "success",
319
+ Util.rightAlignText(`${writeCost.toLocaleString()}ms`, 10),
320
+ ["[","]"]
321
+ ],
163
322
  ]
164
323
 
165
- if(writeStatus === "written") {
324
+ if(writeStatus.description === "written") {
166
325
  status.push(
167
326
  `${outputFilename} written`,
168
327
  ["success", `${writeBytes.toLocaleString()} bytes`, ["[","]"]]
@@ -170,7 +329,7 @@ export default class Session {
170
329
  } else {
171
330
  status.push(
172
331
  `${outputFilename}`,
173
- ["warn", writeStatus.toLocaleUpperCase(), ["[","]"]]
332
+ ["warn", writeStatus.description.toLocaleUpperCase(), ["[","]"]]
174
333
  )
175
334
  }
176
335
 
@@ -198,8 +357,7 @@ export default class Session {
198
357
  error: error.message
199
358
  })
200
359
 
201
- if(error instanceof Sass)
202
- error.report(this.#options.nerd)
360
+ Sass.new("Build process failed.", error).report(this.#options.nerd)
203
361
  } finally {
204
362
  this.#building = false
205
363
  this.#command.asyncEmit("finishedBuilding")
@@ -221,16 +379,20 @@ export default class Session {
221
379
  this.#building = true
222
380
  this.#command.asyncEmit("building")
223
381
 
224
- const changedFile = this.#theme.dependencies.find(dep => dep.path === changed)
382
+ const changedFile = Array.from(this.#theme.getDependencies()).find(
383
+ dep => dep.getSourceFile().path === changed
384
+ )?.getSourceFile()
225
385
 
226
386
  if(!changedFile)
227
387
  return
228
388
 
229
- const fileName = File.relativeOrAbsolutePath(this.#command.cwd, changedFile)
389
+ const fileName = File.relativeOrAbsolutePath(
390
+ this.#command.getCwd(), changedFile
391
+ )
230
392
 
231
393
  const message = [
232
394
  ["info", "REBUILDING", ["[","]"]],
233
- this.#theme.name,
395
+ this.#theme.getName(),
234
396
  ]
235
397
 
236
398
  if(this.#options.nerd)
@@ -262,7 +424,7 @@ export default class Session {
262
424
 
263
425
  Term.status([
264
426
  [builds > 0 ? "success" : "error", "SESSION SUMMARY"],
265
- [builds > 0 ? "info" : "error", this.#theme.name, ["[", "]"]]
427
+ [builds > 0 ? "info" : "error", this.#theme.getName(), ["[", "]"]]
266
428
  ], this.#options)
267
429
 
268
430
  Term.status([
@@ -277,7 +439,8 @@ export default class Session {
277
439
 
278
440
  if(this.#history.length > 0) {
279
441
  const lastBuild = this.#history[this.#history.length - 1]
280
- const totalTime = lastBuild.loadTime + lastBuild.buildTime + lastBuild.writeTime
442
+ const totalTime = lastBuild.loadTime +
443
+ lastBuild.buildTime + lastBuild.writeTime
281
444
 
282
445
  Term.status([
283
446
  [builds > 0 ? "info" : "muted", "Last Build", ["[", "]"]],
@@ -318,10 +481,13 @@ export default class Session {
318
481
  if(this.#watcher)
319
482
  await this.#watcher.close()
320
483
 
321
- const dependencies = this.#theme.dependencies.map(d => d.path)
484
+ const dependencies = Array.from(this.#theme
485
+ .getDependencies())
486
+ .map(d => d.getSourceFile().path)
487
+
322
488
  this.#watcher = chokidar.watch(dependencies, {
323
489
  // Prevent watching own output files
324
- ignored: [this.#theme.outputFileName],
490
+ ignored: [this.#theme.getOutputFileName()],
325
491
  // Add some stability options
326
492
  awaitWriteFinish: {
327
493
  stabilityThreshold: 100,
package/src/Term.js CHANGED
@@ -9,7 +9,7 @@ export default class Term {
9
9
  /**
10
10
  * Log an informational message.
11
11
  *
12
- * @param {...any} arg - Values to log.
12
+ * @param {...unknown} [arg] - Values to log.
13
13
  */
14
14
  static log(...arg) {
15
15
  console.log(...arg)
@@ -18,7 +18,7 @@ export default class Term {
18
18
  /**
19
19
  * Log an informational message.
20
20
  *
21
- * @param {...any} arg - Values to log.
21
+ * @param {...unknown} [arg] - Values to log.
22
22
  */
23
23
  static info(...arg) {
24
24
  console.info(...arg)
@@ -27,16 +27,16 @@ export default class Term {
27
27
  /**
28
28
  * Log a warning message.
29
29
  *
30
- * @param {any} msg - Warning text / object.
30
+ * @param {...unknown} [arg] - Warning text / object.
31
31
  */
32
- static warn(...msg) {
33
- console.warn(...msg)
32
+ static warn(...arg) {
33
+ console.warn(...arg)
34
34
  }
35
35
 
36
36
  /**
37
37
  * Log an error message (plus optional details).
38
38
  *
39
- * @param {...any} arg - Values to log.
39
+ * @param {...unknown} [arg] - Values to log.
40
40
  */
41
41
  static error(...arg) {
42
42
  console.error(...arg)
@@ -45,7 +45,7 @@ export default class Term {
45
45
  /**
46
46
  * Log a debug message (no-op unless console.debug provided/visible by env).
47
47
  *
48
- * @param {...any} arg - Values to log.
48
+ * @param {...unknown} [arg] - Values to log.
49
49
  */
50
50
  static debug(...arg) {
51
51
  console.debug(...arg)