create-bl-theme 1.0.8 → 1.0.10
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/.claude/settings.local.json +3 -1
- package/CLAUDE.md +73 -0
- package/README.md +1 -1
- package/bin/cli.js +122 -22
- package/package.json +1 -1
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(cat:*)",
|
|
5
|
-
"Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js bump:*)"
|
|
5
|
+
"Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js bump:*)",
|
|
6
|
+
"Bash(npm:*)",
|
|
7
|
+
"Bash(node /Users/boidu/Developer/create-bl-theme/bin/cli.js:*)"
|
|
6
8
|
],
|
|
7
9
|
"deny": [],
|
|
8
10
|
"ask": []
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**create-bl-theme** is a CLI tool for scaffolding and managing themes for the Better Lyrics browser extension. It provides commands to create new themes, validate their structure, manage versioning, and handle publishing to a theme registry.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Run the CLI locally during development
|
|
13
|
+
node bin/cli.js [command]
|
|
14
|
+
|
|
15
|
+
# Create a new theme (interactive prompts)
|
|
16
|
+
node bin/cli.js [theme-name]
|
|
17
|
+
|
|
18
|
+
# Validate a theme (supports local paths or GitHub URLs)
|
|
19
|
+
node bin/cli.js validate [dir|url]
|
|
20
|
+
|
|
21
|
+
# Check publishing status against the theme registry
|
|
22
|
+
node bin/cli.js publish [dir]
|
|
23
|
+
|
|
24
|
+
# Bump version (patch/minor/major)
|
|
25
|
+
node bin/cli.js bump [patch|minor|major] [dir]
|
|
26
|
+
|
|
27
|
+
# Show version/help
|
|
28
|
+
node bin/cli.js --version
|
|
29
|
+
node bin/cli.js --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Architecture
|
|
33
|
+
|
|
34
|
+
The entire CLI is implemented in a single file: `bin/cli.js` (ES6 module).
|
|
35
|
+
|
|
36
|
+
**Main routing pattern:**
|
|
37
|
+
- `main()` parses `process.argv` and routes to command handlers
|
|
38
|
+
- Commands: `create()`, `validate()`, `publish()`, `bump()`
|
|
39
|
+
|
|
40
|
+
**Key external dependencies:**
|
|
41
|
+
- `rics` - RICS CSS preprocessor for syntax validation via `compileWithDetails()`
|
|
42
|
+
- `prompts` - Interactive CLI prompts
|
|
43
|
+
- `image-size` - Image dimension validation
|
|
44
|
+
- `picocolors` - Terminal colors
|
|
45
|
+
|
|
46
|
+
**External API calls:**
|
|
47
|
+
- Theme registry: `https://raw.githubusercontent.com/better-lyrics/themes/master/index.json`
|
|
48
|
+
- Lock file: `https://raw.githubusercontent.com/better-lyrics/themes/master/index.lock.json`
|
|
49
|
+
|
|
50
|
+
## Theme Structure Generated
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
theme-name/
|
|
54
|
+
├── metadata.json # Required: id, title, creators, minVersion, hasShaders, version, images
|
|
55
|
+
├── style.rics # Required: styles using RICS preprocessor (plain CSS is valid)
|
|
56
|
+
├── images/ # Required: at least one screenshot
|
|
57
|
+
├── DESCRIPTION.md # Optional: rich markdown description
|
|
58
|
+
├── shader.json # Required if hasShaders: true
|
|
59
|
+
└── README.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Validation Rules
|
|
63
|
+
|
|
64
|
+
- `metadata.json` requires fields: id, title, creators, minVersion, hasShaders, version, images
|
|
65
|
+
- Version must be strict semver (e.g., `1.0.0`)
|
|
66
|
+
- Image IDs: lowercase letters, numbers, hyphens only
|
|
67
|
+
- Images: PNG, JPG, GIF, WebP; warns if not 16:9 aspect ratio
|
|
68
|
+
- RICS syntax validated with detailed error reporting
|
|
69
|
+
- Shader.json required when `hasShaders: true`
|
|
70
|
+
|
|
71
|
+
## RICS Notes
|
|
72
|
+
|
|
73
|
+
RICS is a lightweight CSS preprocessor with full CSS parity. Plain CSS is valid RICS. Supports variables, nesting, mixins. Documentation: https://github.com/better-lyrics/rics
|
package/README.md
CHANGED
|
@@ -164,7 +164,7 @@ Better Lyrics supports **GitHub Flavored Markdown (GFM)** in DESCRIPTION.md, so
|
|
|
164
164
|
|
|
165
165
|
### Auto-Publishing
|
|
166
166
|
|
|
167
|
-
After your theme is registered, install the [Better Lyrics
|
|
167
|
+
After your theme is registered, install the [Better Lyrics Harmonizer](https://github.com/apps/better-lyrics-harmonizer/installations/new) GitHub App on your repo. This enables automatic updates:
|
|
168
168
|
|
|
169
169
|
1. Bump your version: `create-bl-theme bump patch`
|
|
170
170
|
2. Commit and push
|
package/bin/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import path from "path";
|
|
|
7
7
|
import os from "os";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import { imageSize } from "image-size";
|
|
10
|
-
import { execSync } from "child_process";
|
|
10
|
+
import { execSync, spawn } from "child_process";
|
|
11
11
|
import { compileWithDetails } from "rics";
|
|
12
12
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -22,11 +22,78 @@ const RECOMMENDED_HEIGHT = 720;
|
|
|
22
22
|
// GitHub URL patterns
|
|
23
23
|
const GITHUB_URL_PATTERN = /^(?:https?:\/\/)?(?:www\.)?github\.com\/([^\/]+)\/([^\/]+)(?:\/)?(?:\.git)?$/;
|
|
24
24
|
|
|
25
|
+
// GitHub App URL for auto-updates
|
|
26
|
+
const GITHUB_APP_URL = "https://github.com/apps/better-lyrics-harmonizer/installations/new";
|
|
27
|
+
|
|
25
28
|
// Load package.json for version
|
|
26
29
|
const pkg = JSON.parse(
|
|
27
30
|
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8")
|
|
28
31
|
);
|
|
29
32
|
|
|
33
|
+
// Cross-platform browser opener
|
|
34
|
+
function openBrowser(url) {
|
|
35
|
+
const platform = process.platform;
|
|
36
|
+
let command;
|
|
37
|
+
let args;
|
|
38
|
+
|
|
39
|
+
if (platform === "darwin") {
|
|
40
|
+
command = "open";
|
|
41
|
+
args = [url];
|
|
42
|
+
} else if (platform === "win32") {
|
|
43
|
+
command = "cmd";
|
|
44
|
+
args = ["/c", "start", "", url];
|
|
45
|
+
} else {
|
|
46
|
+
command = "xdg-open";
|
|
47
|
+
args = [url];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const child = spawn(command, args, {
|
|
52
|
+
detached: true,
|
|
53
|
+
stdio: "ignore",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
child.on("error", (err) => {
|
|
57
|
+
reject(err);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
child.unref();
|
|
61
|
+
resolve();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Prompt to install GitHub App for auto-updates
|
|
66
|
+
async function promptGitHubAppInstall() {
|
|
67
|
+
console.log();
|
|
68
|
+
console.log(pc.bold("GitHub App Installation"));
|
|
69
|
+
console.log();
|
|
70
|
+
console.log("Install Better Lyrics Harmonizer for automatic theme publishing.");
|
|
71
|
+
console.log(pc.dim("This is a one-time setup. After installing, push changes with a bumped"));
|
|
72
|
+
console.log(pc.dim("version and they'll auto-publish to the theme store (with commit status)."));
|
|
73
|
+
console.log();
|
|
74
|
+
|
|
75
|
+
const response = await prompts({
|
|
76
|
+
type: "confirm",
|
|
77
|
+
name: "openBrowser",
|
|
78
|
+
message: "Open browser to install Better Lyrics Harmonizer?",
|
|
79
|
+
initial: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (response.openBrowser) {
|
|
83
|
+
try {
|
|
84
|
+
await openBrowser(GITHUB_APP_URL);
|
|
85
|
+
console.log(pc.green(" Browser opened!"));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.log(pc.yellow(" Could not open browser automatically."));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(pc.dim(" Install URL:"));
|
|
93
|
+
console.log(pc.cyan(` ${GITHUB_APP_URL}`));
|
|
94
|
+
console.log();
|
|
95
|
+
}
|
|
96
|
+
|
|
30
97
|
async function main() {
|
|
31
98
|
const args = process.argv.slice(2);
|
|
32
99
|
const command = args[0];
|
|
@@ -103,7 +170,28 @@ ${pc.bold("Documentation:")}
|
|
|
103
170
|
`);
|
|
104
171
|
}
|
|
105
172
|
|
|
173
|
+
function isDirectoryEmpty(dirPath) {
|
|
174
|
+
const entries = fs.readdirSync(dirPath);
|
|
175
|
+
return entries.every((entry) => entry.startsWith("."));
|
|
176
|
+
}
|
|
177
|
+
|
|
106
178
|
async function create(targetDir) {
|
|
179
|
+
if (targetDir) {
|
|
180
|
+
const earlyPath = path.resolve(process.cwd(), targetDir);
|
|
181
|
+
if (fs.existsSync(earlyPath) && !isDirectoryEmpty(earlyPath)) {
|
|
182
|
+
const { proceed } = await prompts({
|
|
183
|
+
type: "confirm",
|
|
184
|
+
name: "proceed",
|
|
185
|
+
message: `Directory "${targetDir}" is not empty. Continue anyway? (files may be overwritten)`,
|
|
186
|
+
initial: false,
|
|
187
|
+
});
|
|
188
|
+
if (!proceed) {
|
|
189
|
+
console.log(pc.red("\nCancelled."));
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
107
195
|
const questions = [
|
|
108
196
|
{
|
|
109
197
|
type: targetDir ? null : "text",
|
|
@@ -187,9 +275,17 @@ async function create(targetDir) {
|
|
|
187
275
|
const dir = targetDir || response.directory;
|
|
188
276
|
const fullPath = path.resolve(process.cwd(), dir);
|
|
189
277
|
|
|
190
|
-
if (fs.existsSync(fullPath)) {
|
|
191
|
-
|
|
192
|
-
|
|
278
|
+
if (!targetDir && fs.existsSync(fullPath) && !isDirectoryEmpty(fullPath)) {
|
|
279
|
+
const { proceed } = await prompts({
|
|
280
|
+
type: "confirm",
|
|
281
|
+
name: "proceed",
|
|
282
|
+
message: `Directory "${dir}" is not empty. Continue anyway? (files may be overwritten)`,
|
|
283
|
+
initial: false,
|
|
284
|
+
});
|
|
285
|
+
if (!proceed) {
|
|
286
|
+
console.log(pc.red("\nCancelled."));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
193
289
|
}
|
|
194
290
|
|
|
195
291
|
console.log();
|
|
@@ -308,12 +404,17 @@ MIT
|
|
|
308
404
|
console.log(pc.green(" Theme scaffolded successfully!"));
|
|
309
405
|
console.log();
|
|
310
406
|
console.log(pc.bold(" Next steps:"));
|
|
311
|
-
|
|
312
|
-
|
|
407
|
+
let stepNum = 1;
|
|
408
|
+
if (dir !== ".") {
|
|
409
|
+
console.log(` ${pc.dim(`${stepNum}.`)} cd ${dir}`);
|
|
410
|
+
stepNum++;
|
|
411
|
+
}
|
|
412
|
+
console.log(` ${pc.dim(`${stepNum}.`)} Edit ${pc.cyan("style.rics")} with your theme styles`);
|
|
413
|
+
stepNum++;
|
|
313
414
|
console.log(
|
|
314
|
-
` ${pc.dim(
|
|
415
|
+
` ${pc.dim(`${stepNum}.`)} Add a preview screenshot to ${pc.cyan("images/preview.png")}`
|
|
315
416
|
);
|
|
316
|
-
|
|
417
|
+
stepNum++;
|
|
317
418
|
if (response.useDescriptionFile) {
|
|
318
419
|
console.log(` ${pc.dim(`${stepNum}.`)} Edit ${pc.cyan("DESCRIPTION.md")} with your theme description`);
|
|
319
420
|
stepNum++;
|
|
@@ -617,6 +718,11 @@ async function validate(dir) {
|
|
|
617
718
|
|
|
618
719
|
console.log();
|
|
619
720
|
|
|
721
|
+
// Prompt to install GitHub App if validation passed
|
|
722
|
+
if (errors.length === 0) {
|
|
723
|
+
await promptGitHubAppInstall();
|
|
724
|
+
}
|
|
725
|
+
|
|
620
726
|
// Cleanup temp directory if we cloned from GitHub
|
|
621
727
|
if (tempDir) {
|
|
622
728
|
try {
|
|
@@ -701,30 +807,23 @@ async function publish(dir) {
|
|
|
701
807
|
console.log(` 2. Add { "repo": "${repo}" } to index.json`);
|
|
702
808
|
console.log(" 3. Open a pull request");
|
|
703
809
|
console.log();
|
|
704
|
-
console.log("After your PR is merged, install the GitHub App for auto-updates:");
|
|
705
|
-
|
|
810
|
+
console.log(pc.dim("After your PR is merged, install the GitHub App for auto-updates:"));
|
|
811
|
+
await promptGitHubAppInstall();
|
|
706
812
|
process.exit(0);
|
|
707
813
|
}
|
|
708
814
|
|
|
709
815
|
// 7. Theme is registered
|
|
710
816
|
console.log(pc.green("Theme is registered in the theme store."));
|
|
711
817
|
console.log();
|
|
712
|
-
console.log(pc.bold("
|
|
713
|
-
console.log();
|
|
714
|
-
console.log(
|
|
715
|
-
console.log("
|
|
716
|
-
console.log(pc.cyan(" https://github.com/marketplace/better-lyrics-themes"));
|
|
717
|
-
console.log();
|
|
718
|
-
console.log(" 2. Push changes to your repo:");
|
|
719
|
-
console.log(pc.dim(" git add ."));
|
|
720
|
-
console.log(pc.dim(' git commit -m "feat: update theme"'));
|
|
721
|
-
console.log(pc.dim(" git push"));
|
|
818
|
+
console.log(pc.bold("To publish updates:"));
|
|
819
|
+
console.log(pc.dim(" git add ."));
|
|
820
|
+
console.log(pc.dim(' git commit -m "feat: update theme"'));
|
|
821
|
+
console.log(pc.dim(" git push"));
|
|
722
822
|
console.log();
|
|
723
823
|
console.log("The registry will automatically:");
|
|
724
824
|
console.log(" - Validate your theme");
|
|
725
825
|
console.log(" - Update the lockfile");
|
|
726
826
|
console.log(" - Vendor your theme files");
|
|
727
|
-
console.log(" - Show a commit status on your push");
|
|
728
827
|
console.log();
|
|
729
828
|
|
|
730
829
|
// 8. Check current lockfile status
|
|
@@ -766,7 +865,8 @@ async function publish(dir) {
|
|
|
766
865
|
}
|
|
767
866
|
}
|
|
768
867
|
|
|
769
|
-
|
|
868
|
+
// Prompt to install GitHub App for auto-updates
|
|
869
|
+
await promptGitHubAppInstall();
|
|
770
870
|
}
|
|
771
871
|
|
|
772
872
|
function checkIsGitRepo(themePath) {
|