@gesslar/sassy 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/BuildCommand.js +40 -19
- package/src/Colour.js +6 -1
- package/src/Command.js +13 -7
- package/src/Compiler.js +18 -9
- package/src/LintCommand.js +4 -0
- package/src/Session.js +68 -36
- package/src/Theme.js +24 -7
- package/src/cli.js +12 -32
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"name": "gesslar",
|
|
6
6
|
"url": "https://gesslar.dev"
|
|
7
7
|
},
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.2.0",
|
|
9
9
|
"license": "Unlicense",
|
|
10
10
|
"homepage": "https://github.com/gesslar/sassy#readme",
|
|
11
11
|
"repository": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@gesslar/colours": "^0.8.0",
|
|
47
|
-
"@gesslar/toolkit": "^3.
|
|
47
|
+
"@gesslar/toolkit": "^3.29.0",
|
|
48
48
|
"chokidar": "^5.0.0",
|
|
49
49
|
"color-support": "^1.1.3",
|
|
50
50
|
"commander": "^14.0.2",
|
|
@@ -54,13 +54,14 @@
|
|
|
54
54
|
"yaml": "^2.8.2"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@gesslar/uglier": "^1.
|
|
57
|
+
"@gesslar/uglier": "^1.2.0",
|
|
58
58
|
"eslint": "^9.39.2",
|
|
59
59
|
"typescript": "^5.9.3"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"clean": "rm -rfv ./dist",
|
|
63
63
|
"build": "mkdir -pv ./dist && pnpm pack --pack-destination ./dist/",
|
|
64
|
+
"types": "node -e \"require('fs').rmSync('types',{recursive:true,force:true});\" && tsc -p tsconfig.types.json",
|
|
64
65
|
"exec": "node ./src/cli.js",
|
|
65
66
|
"lint": "eslint src/",
|
|
66
67
|
"test": "node --test tests/**/*.test.js",
|
package/src/BuildCommand.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {EventEmitter} from "node:events"
|
|
2
2
|
import process from "node:process"
|
|
3
3
|
|
|
4
|
-
import {Sass, Term, Util} from "@gesslar/toolkit"
|
|
4
|
+
import {Promised, Sass, Term, Util} from "@gesslar/toolkit"
|
|
5
5
|
import Command from "./Command.js"
|
|
6
6
|
import Session from "./Session.js"
|
|
7
7
|
import Theme from "./Theme.js"
|
|
@@ -36,6 +36,18 @@ export default class BuildCommand extends Command {
|
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Emits an event asynchronously using the internal emitter.
|
|
41
|
+
* This method wraps Util.asyncEmit for convenience.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} event - The event name to emit
|
|
44
|
+
* @param {...any} args - Arguments to pass to the event handlers
|
|
45
|
+
* @returns {Promise<void>} Resolves when all event handlers have completed
|
|
46
|
+
*/
|
|
47
|
+
async asyncEmit(event, ...args) {
|
|
48
|
+
return await Util.asyncEmit(this.emitter, event, ...args)
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
/**
|
|
40
52
|
* Executes the build command for the provided theme files.
|
|
41
53
|
* Processes each file in parallel, optionally watching for changes.
|
|
@@ -43,13 +55,21 @@ export default class BuildCommand extends Command {
|
|
|
43
55
|
* @param {string[]} fileNames - Array of theme file paths to process
|
|
44
56
|
* @param {object} options - Build options
|
|
45
57
|
* @param {boolean} [options.watch] - Enable watch mode for file changes
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
* @param {string} [options.outputDir] - Custom output directory path
|
|
59
|
+
* @param {boolean} [options.dryRun] - Print JSON to stdout without writing files
|
|
60
|
+
* @param {boolean} [options.silent] - Silent mode, only show errors or dry-run output
|
|
49
61
|
* @returns {Promise<void>} Resolves when all files are processed
|
|
50
62
|
* @throws {Error} When theme compilation fails
|
|
51
63
|
*/
|
|
52
64
|
async execute(fileNames, options) {
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {object} BuildCommandOptions
|
|
68
|
+
* @property {boolean} [watch] Enable watch mode for file changes
|
|
69
|
+
* @property {string} [outputDir] Custom output directory path
|
|
70
|
+
* @property {boolean} [dryRun] Print JSON to stdout without writing files
|
|
71
|
+
* @property {boolean} [silent] Silent mode, only show errors or dry-run output
|
|
72
|
+
*/
|
|
53
73
|
const cwd = this.getCwd()
|
|
54
74
|
|
|
55
75
|
if(options.watch) {
|
|
@@ -64,7 +84,7 @@ export default class BuildCommand extends Command {
|
|
|
64
84
|
this.emitter.on("printPrompt", () => this.#printPrompt())
|
|
65
85
|
}
|
|
66
86
|
|
|
67
|
-
const sessionResults = await
|
|
87
|
+
const sessionResults = await Promised.settle(
|
|
68
88
|
fileNames.map(async fileName => {
|
|
69
89
|
const fileObject = await this.resolveThemeFileName(fileName, cwd)
|
|
70
90
|
const theme = new Theme(fileObject, cwd, options)
|
|
@@ -74,24 +94,25 @@ export default class BuildCommand extends Command {
|
|
|
74
94
|
})
|
|
75
95
|
)
|
|
76
96
|
|
|
77
|
-
if(
|
|
78
|
-
|
|
97
|
+
if(Promised.hasRejected(sessionResults))
|
|
98
|
+
Promised.throw("Creating sessions.", sessionResults)
|
|
79
99
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
100
|
+
// if(sessionResults.some(theme => theme.status === "rejected")) {
|
|
101
|
+
// const rejected = sessionResults.filter(result => result.status === "rejected")
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
// rejected.forEach(item => {
|
|
104
|
+
// const sassError = Sass.new("Creating session for theme file.", item.reason)
|
|
105
|
+
// sassError.report(options.nerd)
|
|
106
|
+
// })
|
|
107
|
+
// process.exit(1)
|
|
108
|
+
// }
|
|
88
109
|
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
const sessions = Promised.values(sessionResults)
|
|
111
|
+
const firstRun = await Promised.settle(sessions.map(
|
|
112
|
+
async session => await session.run()))
|
|
91
113
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
114
|
+
if(Promised.hasRejected(firstRun))
|
|
115
|
+
Promised.throw("Executing sessions.", firstRun)
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
/**
|
package/src/Colour.js
CHANGED
|
@@ -13,7 +13,12 @@ import {
|
|
|
13
13
|
parse
|
|
14
14
|
} from "culori"
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {Sass, Util} from "@gesslar/toolkit"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @import {ThemeToken} from "./ThemeToken.js"
|
|
20
|
+
*/
|
|
21
|
+
|
|
17
22
|
// Cache for parsed colours to improve performance
|
|
18
23
|
const _colourCache = new Map()
|
|
19
24
|
|
package/src/Command.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {FileSystem, Sass} from "@gesslar/toolkit"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @import {DirectoryObject} from "@gesslar/toolkit"
|
|
5
|
+
* @import {FileObject} from "@gesslar/toolkit"
|
|
6
|
+
* @import {Cache} from "@gesslar/toolkit"
|
|
7
|
+
*/
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Base class for command-line interface commands.
|
|
@@ -17,11 +23,10 @@ export default class Command {
|
|
|
17
23
|
/**
|
|
18
24
|
* Creates a new Command instance.
|
|
19
25
|
*
|
|
20
|
-
* @param {object} config - Configuration object
|
|
21
26
|
* @param {DirectoryObject} config.cwd - Current working directory object
|
|
22
27
|
* @param {object} config.packageJson - Package.json data
|
|
23
28
|
*/
|
|
24
|
-
constructor({cwd,packageJson}) {
|
|
29
|
+
constructor({cwd, packageJson}) {
|
|
25
30
|
this.#cwd = cwd
|
|
26
31
|
this.#packageJson = packageJson
|
|
27
32
|
}
|
|
@@ -221,17 +226,18 @@ export default class Command {
|
|
|
221
226
|
* Resolves a theme file name to a FileObject and validates its existence.
|
|
222
227
|
*
|
|
223
228
|
* @param {string} fileName - The theme file name or path
|
|
224
|
-
* @param {
|
|
229
|
+
* @param {DirectoryObject} cwd - The current working directory object
|
|
225
230
|
* @returns {Promise<FileObject>} The resolved and validated FileObject
|
|
226
231
|
* @throws {Sass} If the file does not exist
|
|
227
232
|
*/
|
|
228
233
|
async resolveThemeFileName(fileName, cwd) {
|
|
229
|
-
|
|
234
|
+
fileName = FileSystem.relativeOrAbsolutePath(cwd.path, fileName)
|
|
235
|
+
|
|
236
|
+
const fileObject = cwd.getFile(fileName)
|
|
230
237
|
|
|
231
238
|
if(!await fileObject.exists)
|
|
232
|
-
throw Sass.new(`No such file 🤷: ${fileObject.
|
|
239
|
+
throw Sass.new(`No such file 🤷: ${fileObject.relativeTo(cwd)}`)
|
|
233
240
|
|
|
234
241
|
return fileObject
|
|
235
242
|
}
|
|
236
|
-
|
|
237
243
|
}
|
package/src/Compiler.js
CHANGED
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
* Supports extension points for custom phases and output formats.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {Collection, Data,
|
|
14
|
+
import {Collection, Data, Sass, Term, Util} from "@gesslar/toolkit"
|
|
15
|
+
|
|
15
16
|
import Evaluator from "./Evaluator.js"
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @import {Theme} from "./Theme.js"
|
|
20
|
+
*/
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Main compiler class for processing theme source files.
|
|
19
24
|
* Handles the complete compilation pipeline from source to VS Code theme output.
|
|
@@ -39,6 +44,7 @@ export default class Compiler {
|
|
|
39
44
|
const config = this.#decomposeObject(sourceConfig)
|
|
40
45
|
|
|
41
46
|
evaluate(config)
|
|
47
|
+
|
|
42
48
|
const recompConfig = this.#composeObject(config)
|
|
43
49
|
|
|
44
50
|
const header = {
|
|
@@ -49,12 +55,11 @@ export default class Compiler {
|
|
|
49
55
|
|
|
50
56
|
// Let's get all of the imports!
|
|
51
57
|
const imports = recompConfig.import ?? []
|
|
52
|
-
const {imported,importByFile} =
|
|
53
|
-
await this.#import(imports, theme)
|
|
58
|
+
const {imported,importByFile} = await this.#import(imports, theme)
|
|
54
59
|
|
|
55
|
-
importByFile.forEach(
|
|
56
|
-
theme.addDependency(file,themeData)
|
|
57
|
-
|
|
60
|
+
importByFile.forEach(
|
|
61
|
+
(themeData, file) => theme.addDependency(file,themeData)
|
|
62
|
+
)
|
|
58
63
|
|
|
59
64
|
// Handle tokenColors separately - imports first, then main source
|
|
60
65
|
// (append-only)
|
|
@@ -149,15 +154,18 @@ export default class Compiler {
|
|
|
149
154
|
: imports
|
|
150
155
|
|
|
151
156
|
if(!Collection.isArrayUniform(imports, "string"))
|
|
152
|
-
throw new
|
|
157
|
+
throw Sass.new(
|
|
153
158
|
`All import entries must be strings. Got ${JSON.stringify(imports)}`
|
|
154
159
|
)
|
|
155
160
|
|
|
156
161
|
const loaded = new Map()
|
|
157
162
|
|
|
163
|
+
const themeSource = theme.getSourceFile()
|
|
164
|
+
const themeDirectory = themeSource.parent
|
|
165
|
+
|
|
158
166
|
for(const importing of imports) {
|
|
159
167
|
try {
|
|
160
|
-
const file =
|
|
168
|
+
const file = themeDirectory.getFile(importing)
|
|
161
169
|
|
|
162
170
|
// Get the cached version or a new version. Who knows? I don't know.
|
|
163
171
|
const {result, cost} = await Util.time(async() => {
|
|
@@ -165,10 +173,11 @@ export default class Compiler {
|
|
|
165
173
|
})
|
|
166
174
|
|
|
167
175
|
if(theme.getOptions().nerd) {
|
|
176
|
+
const cwd = theme.getCwd()
|
|
168
177
|
Term.status([
|
|
169
178
|
["muted", Util.rightAlignText(`${cost.toLocaleString()}ms`, 10), ["[","]"]],
|
|
170
179
|
"",
|
|
171
|
-
["muted", `${file.
|
|
180
|
+
["muted", `${file.relativeTo(cwd)}`],
|
|
172
181
|
["muted", `${theme.getName()}`,["(",")"]],
|
|
173
182
|
], theme.getOptions())
|
|
174
183
|
}
|
package/src/LintCommand.js
CHANGED
|
@@ -23,6 +23,10 @@ import Evaluator from "./Evaluator.js"
|
|
|
23
23
|
import Theme from "./Theme.js"
|
|
24
24
|
import {Term} from "@gesslar/toolkit"
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @import {ThemePool} from "./ThemePool.js"
|
|
28
|
+
*/
|
|
29
|
+
|
|
26
30
|
// oops, need to have @gesslar/colours support this, too!
|
|
27
31
|
// ansiColors.enabled = colorSupport.hasBasic
|
|
28
32
|
|
package/src/Session.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import chokidar from "chokidar"
|
|
2
|
+
import path from "node:path"
|
|
2
3
|
|
|
3
|
-
import {Sass, Term, Util} from "@gesslar/toolkit"
|
|
4
|
+
import {Promised, Sass, Term, Util} from "@gesslar/toolkit"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @import {Command} from "./Command.js"
|
|
8
|
+
* @import {Theme} from "./Theme.js"
|
|
9
|
+
*/
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* @typedef {object} SessionOptions
|
|
@@ -19,6 +25,11 @@ import {Sass, Term, Util} from "@gesslar/toolkit"
|
|
|
19
25
|
* @property {string} [error] - Error message when success is false
|
|
20
26
|
*/
|
|
21
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @import {Theme} from "./Theme.js"
|
|
30
|
+
* @import {Command} from "./Command.js"
|
|
31
|
+
*/
|
|
32
|
+
|
|
22
33
|
export default class Session {
|
|
23
34
|
/**
|
|
24
35
|
* The theme instance managed by this session.
|
|
@@ -170,8 +181,9 @@ export default class Session {
|
|
|
170
181
|
async run() {
|
|
171
182
|
|
|
172
183
|
this.#building = true
|
|
184
|
+
|
|
173
185
|
await this.#command.asyncEmit("building")
|
|
174
|
-
this.#command.asyncEmit("recordBuildStart", this.#theme)
|
|
186
|
+
await this.#command.asyncEmit("recordBuildStart", this.#theme)
|
|
175
187
|
await this.#buildPipeline()
|
|
176
188
|
|
|
177
189
|
// This must come after, or you will fuck up the watching!
|
|
@@ -224,6 +236,7 @@ export default class Session {
|
|
|
224
236
|
`${this.#theme.getName()} loaded`,
|
|
225
237
|
["info", `${bytes.toLocaleString()} bytes`, ["[","]"]]
|
|
226
238
|
], this.#options)
|
|
239
|
+
|
|
227
240
|
/**
|
|
228
241
|
* ****************************************************************
|
|
229
242
|
* Have the theme build itself.
|
|
@@ -236,31 +249,24 @@ export default class Session {
|
|
|
236
249
|
.map(d => d.getSourceFile())
|
|
237
250
|
.filter(f => f != null) // Filter out any null/undefined files
|
|
238
251
|
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const fileName = fileObject.toString()
|
|
246
|
-
const fileSize = await fileObject.size()
|
|
252
|
+
const cwd = this.#theme.getCwd()
|
|
253
|
+
const settled = await Promised
|
|
254
|
+
.settle(dependencyFiles.map(async fileObject => {
|
|
255
|
+
if(!fileObject)
|
|
256
|
+
throw Sass.new("Invalid dependency file object")
|
|
247
257
|
|
|
248
|
-
return [
|
|
258
|
+
return [fileObject.relativeTo(cwd), await fileObject.size()]
|
|
249
259
|
}))
|
|
250
260
|
|
|
251
|
-
|
|
261
|
+
if(Promised.hasRejected(settled))
|
|
262
|
+
throw Sass.new("Compiling dependencies.", settled)
|
|
252
263
|
|
|
253
|
-
|
|
254
|
-
rejected.forEach(reject => Term.error(reject.reason))
|
|
255
|
-
throw new Error("Compilation failed")
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const dependencies = compileResult
|
|
264
|
+
const dependencies = Promised.values(settled)
|
|
259
265
|
.slice(1)
|
|
260
266
|
.map(dep => dep?.value)
|
|
261
267
|
.filter(Boolean)
|
|
262
268
|
|
|
263
|
-
const totalBytes =
|
|
269
|
+
const totalBytes = settled.reduce(
|
|
264
270
|
(acc,curr) => acc + (curr?.value[1] ?? 0), 0
|
|
265
271
|
)
|
|
266
272
|
|
|
@@ -271,7 +277,7 @@ export default class Session {
|
|
|
271
277
|
["[","]"]
|
|
272
278
|
],
|
|
273
279
|
`${this.#theme.getName()} compiled`,
|
|
274
|
-
["success", `${
|
|
280
|
+
["success", `${settled[0].value[1].toLocaleString()} bytes`, ["[","]"]],
|
|
275
281
|
["info", `${totalBytes.toLocaleString()} total bytes`, ["(",")"]],
|
|
276
282
|
], this.#options)
|
|
277
283
|
|
|
@@ -303,7 +309,7 @@ export default class Session {
|
|
|
303
309
|
file: outputFile,
|
|
304
310
|
bytes: writeBytes
|
|
305
311
|
} = writeResult.result
|
|
306
|
-
const outputFilename = outputFile.
|
|
312
|
+
const outputFilename = outputFile.relativeTo(cwd)
|
|
307
313
|
const status = [
|
|
308
314
|
[
|
|
309
315
|
"success",
|
|
@@ -327,7 +333,7 @@ export default class Session {
|
|
|
327
333
|
Term.status(status, this.#options)
|
|
328
334
|
|
|
329
335
|
// Track successful build
|
|
330
|
-
this.#command.asyncEmit("recordBuildSucceed", this.#theme)
|
|
336
|
+
await this.#command.asyncEmit("recordBuildSucceed", this.#theme)
|
|
331
337
|
this.#history.push({
|
|
332
338
|
timestamp: buildStart,
|
|
333
339
|
loadTime: loadCost,
|
|
@@ -348,36 +354,47 @@ export default class Session {
|
|
|
348
354
|
error: error.message
|
|
349
355
|
})
|
|
350
356
|
|
|
351
|
-
Sass.new("Build process failed.", error)
|
|
357
|
+
throw Sass.new("Build process failed.", error)
|
|
352
358
|
} finally {
|
|
353
359
|
this.#building = false
|
|
354
|
-
this.#command.asyncEmit("finishedBuilding")
|
|
360
|
+
await this.#command.asyncEmit("finishedBuilding")
|
|
355
361
|
}
|
|
356
362
|
}
|
|
357
363
|
|
|
358
364
|
/**
|
|
359
365
|
* Handles a file change event and triggers a rebuild for the theme.
|
|
360
366
|
*
|
|
361
|
-
* @param {string} changed - Path to the changed file
|
|
367
|
+
* @param {string} changed - Path to the changed file (from chokidar)
|
|
362
368
|
* @param {object} _stats - OS-level file stat information
|
|
363
369
|
* @returns {Promise<void>}
|
|
364
370
|
*/
|
|
365
371
|
async #handleFileChange(changed, _stats) {
|
|
372
|
+
let startedPipeline = false
|
|
373
|
+
|
|
366
374
|
try {
|
|
367
375
|
if(this.#building)
|
|
368
376
|
return
|
|
369
377
|
|
|
370
378
|
this.#building = true
|
|
371
|
-
this.#command.asyncEmit("building")
|
|
379
|
+
await this.#command.asyncEmit("building")
|
|
380
|
+
|
|
381
|
+
// Normalize the changed path from chokidar for comparison
|
|
382
|
+
const normalizedChanged = path.resolve(changed)
|
|
372
383
|
|
|
373
384
|
const changedFile = Array.from(this.#theme.getDependencies()).find(
|
|
374
|
-
dep =>
|
|
385
|
+
dep => {
|
|
386
|
+
const depPath = dep.getSourceFile().path
|
|
387
|
+
const normalizedDepPath = path.resolve(depPath)
|
|
388
|
+
|
|
389
|
+
return normalizedDepPath === normalizedChanged
|
|
390
|
+
}
|
|
375
391
|
)?.getSourceFile()
|
|
376
392
|
|
|
377
393
|
if(!changedFile)
|
|
378
394
|
return
|
|
379
395
|
|
|
380
|
-
const
|
|
396
|
+
const cwd = this.#theme.getCwd()
|
|
397
|
+
const fileName = changedFile.relativeTo(cwd)
|
|
381
398
|
|
|
382
399
|
const message = [
|
|
383
400
|
["info", "REBUILDING", ["[","]"]],
|
|
@@ -390,17 +407,24 @@ export default class Session {
|
|
|
390
407
|
Term.status(message)
|
|
391
408
|
|
|
392
409
|
await this.#resetWatcher()
|
|
410
|
+
startedPipeline = true
|
|
393
411
|
await this.#buildPipeline()
|
|
412
|
+
} catch(error) {
|
|
413
|
+
const sassError = Sass.new("Handling file change.", error)
|
|
414
|
+
sassError.report(this.#options?.nerd)
|
|
415
|
+
|
|
416
|
+
if(!startedPipeline)
|
|
417
|
+
await this.#command.asyncEmit("finishedBuilding")
|
|
394
418
|
} finally {
|
|
395
419
|
this.#building = false
|
|
396
420
|
}
|
|
397
421
|
}
|
|
398
422
|
|
|
399
423
|
/**
|
|
400
|
-
* Displays a formatted summary of the session's build statistics and
|
|
401
|
-
* Shows total builds, success/failure counts, success rate
|
|
402
|
-
* information from the most recent build. Used during
|
|
403
|
-
* final statistics to the user.
|
|
424
|
+
* Displays a formatted summary of the session's build statistics and
|
|
425
|
+
* performance. Shows total builds, success/failure counts, success rate
|
|
426
|
+
* percentage, and timing information from the most recent build. Used during
|
|
427
|
+
* session cleanup to provide final statistics to the user.
|
|
404
428
|
*
|
|
405
429
|
* @returns {void}
|
|
406
430
|
*/
|
|
@@ -448,14 +472,16 @@ export default class Session {
|
|
|
448
472
|
return
|
|
449
473
|
|
|
450
474
|
try {
|
|
451
|
-
this.#command.asyncEmit("recordBuildStart", this.#theme)
|
|
475
|
+
await this.#command.asyncEmit("recordBuildStart", this.#theme)
|
|
452
476
|
this.#building = true
|
|
453
477
|
await this.#resetWatcher()
|
|
454
|
-
this.#command.asyncEmit("building")
|
|
478
|
+
await this.#command.asyncEmit("building")
|
|
455
479
|
await this.#buildPipeline(true)
|
|
456
480
|
} catch(error) {
|
|
457
481
|
await this.#command.asyncEmit("recordBuildFail", this.#theme)
|
|
458
|
-
|
|
482
|
+
const sassError = Sass.new("Handling rebuild request.", error)
|
|
483
|
+
sassError.report(this.#options?.nerd)
|
|
484
|
+
throw sassError
|
|
459
485
|
} finally {
|
|
460
486
|
this.#building = false
|
|
461
487
|
}
|
|
@@ -470,9 +496,15 @@ export default class Session {
|
|
|
470
496
|
if(this.#watcher)
|
|
471
497
|
await this.#watcher.close()
|
|
472
498
|
|
|
499
|
+
// Get real paths for chokidar (normalized for consistency)
|
|
473
500
|
const dependencies = Array.from(this.#theme
|
|
474
501
|
.getDependencies())
|
|
475
|
-
.map(d =>
|
|
502
|
+
.map(d => {
|
|
503
|
+
const filePath = d.getSourceFile().path
|
|
504
|
+
|
|
505
|
+
// Normalize to absolute path for chokidar
|
|
506
|
+
return path.resolve(filePath)
|
|
507
|
+
})
|
|
476
508
|
|
|
477
509
|
this.#watcher = chokidar.watch(dependencies, {
|
|
478
510
|
// Prevent watching own output files
|
package/src/Theme.js
CHANGED
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
* - Write output files, supporting dry-run and hash-based skip
|
|
14
14
|
* - Support watch mode for live theme development
|
|
15
15
|
*/
|
|
16
|
-
import {Sass,
|
|
16
|
+
import {Sass, Term, Util} from "@gesslar/toolkit"
|
|
17
17
|
import Compiler from "./Compiler.js"
|
|
18
18
|
import ThemePool from "./ThemePool.js"
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @import {Cache} from "@gesslar/toolkit"
|
|
22
|
+
* @import {DirectoryObject} from "@gesslar/toolkit"
|
|
23
|
+
* @import {FileObject} from "@gesslar/toolkit"
|
|
24
|
+
*/
|
|
25
|
+
|
|
20
26
|
const outputFileExtension = "color-theme.json"
|
|
21
27
|
const obviouslyASentinelYouCantMissSoShutUpAboutIt = "kakadoodoo"
|
|
22
28
|
|
|
@@ -39,6 +45,7 @@ export default class Theme {
|
|
|
39
45
|
#sourceFile = null
|
|
40
46
|
#source = null
|
|
41
47
|
#options = null
|
|
48
|
+
|
|
42
49
|
/**
|
|
43
50
|
* The dependencies of this theme.
|
|
44
51
|
*
|
|
@@ -56,6 +63,8 @@ export default class Theme {
|
|
|
56
63
|
#outputJson = null
|
|
57
64
|
#outputFileName = null
|
|
58
65
|
#outputHash = null
|
|
66
|
+
#outputFile = null
|
|
67
|
+
#outputDir = null
|
|
59
68
|
|
|
60
69
|
#cwd = null
|
|
61
70
|
|
|
@@ -72,6 +81,16 @@ export default class Theme {
|
|
|
72
81
|
this.#outputFileName = `${this.#name}.${outputFileExtension}`
|
|
73
82
|
this.#options = options
|
|
74
83
|
this.#cwd = cwd
|
|
84
|
+
|
|
85
|
+
// Let's create the output directory, since we're gonna needs it.
|
|
86
|
+
// If outputDir is not provided or is ".", use the cwd itself
|
|
87
|
+
const outputDir = options.outputDir && options.outputDir !== "."
|
|
88
|
+
? cwd.getDirectory(options.outputDir)
|
|
89
|
+
: cwd
|
|
90
|
+
const outputFile = outputDir.getFile(this.#outputFileName)
|
|
91
|
+
|
|
92
|
+
this.#outputFile = outputFile
|
|
93
|
+
this.#outputDir = outputDir
|
|
75
94
|
}
|
|
76
95
|
|
|
77
96
|
/**
|
|
@@ -479,7 +498,7 @@ export default class Theme {
|
|
|
479
498
|
|
|
480
499
|
if(!source[PropertyKey.CONFIG.description])
|
|
481
500
|
throw Sass.new(
|
|
482
|
-
`Source file does not contain '${PropertyKey.CONFIG.description}' property: ${this.#sourceFile.
|
|
501
|
+
`Source file does not contain '${PropertyKey.CONFIG.description}' property: ${this.#sourceFile.relativeTo(this.#cwd)}`
|
|
483
502
|
)
|
|
484
503
|
|
|
485
504
|
this.#source = source
|
|
@@ -497,7 +516,6 @@ export default class Theme {
|
|
|
497
516
|
*/
|
|
498
517
|
async build() {
|
|
499
518
|
const compiler = new Compiler()
|
|
500
|
-
|
|
501
519
|
await compiler.compile(this)
|
|
502
520
|
|
|
503
521
|
return this
|
|
@@ -512,8 +530,7 @@ export default class Theme {
|
|
|
512
530
|
*/
|
|
513
531
|
async write(force=false) {
|
|
514
532
|
const output = this.#outputJson
|
|
515
|
-
const
|
|
516
|
-
const file = new FileObject(this.#outputFileName, outputDir)
|
|
533
|
+
const file = this.#outputFile
|
|
517
534
|
|
|
518
535
|
if(this.#options.dryRun) {
|
|
519
536
|
Term.log(this.#outputJson)
|
|
@@ -533,8 +550,8 @@ export default class Theme {
|
|
|
533
550
|
}
|
|
534
551
|
|
|
535
552
|
// Real write (timed)
|
|
536
|
-
if(!await outputDir.exists)
|
|
537
|
-
await outputDir.assureExists()
|
|
553
|
+
if(!await this.#outputDir.exists)
|
|
554
|
+
await this.#outputDir.assureExists()
|
|
538
555
|
|
|
539
556
|
await file.write(output)
|
|
540
557
|
|
package/src/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import process from "node:process"
|
|
|
36
36
|
import url from "node:url"
|
|
37
37
|
import c from "@gesslar/colours"
|
|
38
38
|
|
|
39
|
-
import {Cache,
|
|
39
|
+
import {Cache, DirectoryObject, FileObject, Sass, Term} from "@gesslar/toolkit"
|
|
40
40
|
import BuildCommand from "./BuildCommand.js"
|
|
41
41
|
import LintCommand from "./LintCommand.js"
|
|
42
42
|
import ResolveCommand from "./ResolveCommand.js"
|
|
@@ -74,9 +74,11 @@ void (async function main() {
|
|
|
74
74
|
c.alias.set("modified-bracket", "{F165}")
|
|
75
75
|
c.alias.set("muted", "{F240}")
|
|
76
76
|
c.alias.set("muted-bracket", "{F244}")
|
|
77
|
+
|
|
77
78
|
// Lint command
|
|
78
79
|
c.alias.set("context", "{F159}")
|
|
79
80
|
c.alias.set("loc", "{F148}")
|
|
81
|
+
|
|
80
82
|
// Resolve command
|
|
81
83
|
c.alias.set("head", "{F220}")
|
|
82
84
|
c.alias.set("leaf", "{F151}")
|
|
@@ -89,11 +91,12 @@ void (async function main() {
|
|
|
89
91
|
c.alias.set("arrow", "{F033}")
|
|
90
92
|
|
|
91
93
|
const cache = new Cache()
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
const cwd = DirectoryObject.fromCwd()
|
|
95
|
+
const packageJson = new FileObject(
|
|
96
|
+
"package.json",
|
|
97
|
+
url.fileURLToPath(new url.URL("..", import.meta.url))
|
|
98
|
+
)
|
|
99
|
+
const pkgJson = await packageJson.loadData()
|
|
97
100
|
|
|
98
101
|
// These are available to all subcommands in addition to whatever they
|
|
99
102
|
// provide.
|
|
@@ -116,36 +119,13 @@ void (async function main() {
|
|
|
116
119
|
command.addCliOptions(alwaysAvailable, false)
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
// // Add the build subcommand
|
|
120
|
-
// const buildCommand = new BuildCommand({cwd, packageJson: pkgJson})
|
|
121
|
-
|
|
122
|
-
// buildCommand.cache = cache
|
|
123
|
-
|
|
124
|
-
// void(await buildCommand.buildCli(program))
|
|
125
|
-
// .addCliOptions(alwaysAvailable, false)
|
|
126
|
-
|
|
127
|
-
// // Add the resolve subcommand
|
|
128
|
-
// const resolveCommand = new ResolveCommand({cwd, packageJson: pkgJson})
|
|
129
|
-
|
|
130
|
-
// resolveCommand.cache = cache
|
|
131
|
-
|
|
132
|
-
// void(await resolveCommand.buildCli(program))
|
|
133
|
-
// .addCliOptions(alwaysAvailable, false)
|
|
134
|
-
|
|
135
|
-
// // Add the lint subcommand
|
|
136
|
-
// const lintCommand = new LintCommand({cwd, packageJson: pkgJson})
|
|
137
|
-
|
|
138
|
-
// lintCommand.cache = cache
|
|
139
|
-
|
|
140
|
-
// void(await lintCommand.buildCli(program))
|
|
141
|
-
// .addCliOptions(alwaysAvailable, false)
|
|
142
|
-
|
|
143
122
|
// Let'er rip, bitches! VROOM VROOM, motherfucker!!
|
|
144
123
|
await program.parseAsync()
|
|
145
124
|
|
|
146
125
|
} catch(error) {
|
|
147
|
-
Sass
|
|
148
|
-
.
|
|
126
|
+
Sass
|
|
127
|
+
.from(error, "Starting Sassy.")
|
|
128
|
+
.report(sassyOptions.nerd ?? false)
|
|
149
129
|
|
|
150
130
|
process.exit(1)
|
|
151
131
|
}
|