Skip to content

Commit d9ba203

Browse files
authored
Merge pull request #3 from D3SOX/bun-port
Port to Bun
2 parents dffe06e + 23bc4ca commit d9ba203

File tree

9 files changed

+207
-1650
lines changed

9 files changed

+207
-1650
lines changed

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "bun"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
cooldown:
8+
default-days: 7
9+
- package-ecosystem: "github-actions"
10+
directory: "/"
11+
schedule:
12+
interval: "weekly"

.github/workflows/build.yml

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,31 @@ jobs:
1212

1313
steps:
1414
- name: Checkout
15-
uses: actions/checkout@v5
15+
uses: actions/checkout@v6
1616

17-
- uses: pnpm/action-setup@v4
18-
name: Install pnpm
17+
- name: Setup bun
18+
uses: oven-sh/setup-bun@v2
1919
with:
20-
version: 10
21-
run_install: false
20+
bun-version: latest
2221

23-
- name: Install Node.js
24-
uses: actions/setup-node@v5
22+
- name: Cache bun dependencies
23+
uses: actions/cache@v4
2524
with:
26-
node-version: 22
27-
cache: 'pnpm'
25+
path: |
26+
~/.bun/install/cache
27+
~/.cache/bun
28+
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
29+
restore-keys: |
30+
${{ runner.os }}-bun-
2831
2932
- name: Install dependencies
30-
run: pnpm install --frozen-lockfile
33+
run: bun install --frozen-lockfile
3134

3235
- name: Build extension
33-
run: pnpm build-local
36+
run: bun run build-local
3437

3538
- name: Upload dist artifact
36-
uses: actions/upload-artifact@v4
39+
uses: actions/upload-artifact@v6
3740
with:
3841
name: spicetify-queue-manager
3942
path: dist

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ For changes, see [CHANGELOG.md](https://github.com/D3SOX/spicetify-queue-manager
4343

4444
## Development
4545

46-
Requires [Spicetify CLI](https://spicetify.app) and [pnpm](https://pnpm.io).
46+
Requires [Spicetify CLI](https://spicetify.app) and [Bun](https://bun.sh).
4747

4848
```bash
49-
pnpm install
50-
pnpm build # or: pnpm watch
49+
bun install
50+
bun run build # or: bun watch
5151
spicetify extensions queue-manager.js
5252
spicetify apply -n
5353
```

build.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { readFileSync, writeFileSync, existsSync, rmSync, readdirSync, mkdirSync, watch as watchFs } from "node:fs";
2+
import { join, dirname } from "node:path";
3+
4+
const SRC = join(import.meta.dir, "src");
5+
const ENTRY = join(SRC, "app.ts");
6+
7+
type Settings = { nameId: string };
8+
9+
function getSettings(): Settings {
10+
return JSON.parse(readFileSync(join(SRC, "settings.json"), "utf-8"));
11+
}
12+
13+
function getJsOutputName(): string {
14+
return `${getSettings().nameId}.js`;
15+
}
16+
17+
const argv = process.argv.slice(2);
18+
const minify = argv.includes("--minify") || argv.includes("-m");
19+
const watch = argv.includes("--watch") || argv.includes("-w");
20+
21+
function parseOutDir(): string | undefined {
22+
for (let i = 0; i < argv.length; i++) {
23+
if (argv[i] === "--out" || argv[i] === "-o") {
24+
return argv[i + 1];
25+
}
26+
if (argv[i].startsWith("--out=")) {
27+
return argv[i].slice(6);
28+
}
29+
}
30+
return undefined;
31+
}
32+
33+
async function getOutDir(): Promise<string> {
34+
const out = parseOutDir();
35+
if (out) return join(import.meta.dir, out);
36+
const proc = Bun.spawn(["spicetify", "-c"], { stdout: "pipe", stderr: "pipe" });
37+
const text = await new Response(proc.stdout).text();
38+
const ok = await proc.exited;
39+
if (ok !== 0) {
40+
const err = await new Response(proc.stderr).text();
41+
console.error("spicetify -c failed:", err || "spicetify not found or not configured");
42+
process.exit(1);
43+
}
44+
return join(dirname(text.trim()), "Extensions");
45+
}
46+
47+
function getStyleId(): string {
48+
return getSettings().nameId.replace(/-/g, "D");
49+
}
50+
51+
function postProcess(outDir: string): void {
52+
// Bun outputs using entry basename (app.js); we want nameId.js like spicetify-creator
53+
const jsFromBuild = join(outDir, "app.js");
54+
const jsOut = join(outDir, getJsOutputName());
55+
if (!existsSync(jsFromBuild)) return;
56+
57+
let js = readFileSync(jsFromBuild, "utf-8");
58+
if (jsOut !== jsFromBuild) rmSync(jsFromBuild);
59+
60+
const styleId = getStyleId();
61+
62+
const files = readdirSync(outDir, { withFileTypes: true });
63+
for (const f of files) {
64+
if (f.isFile() && f.name.endsWith(".css")) {
65+
const cssPath = join(outDir, f.name);
66+
const css = readFileSync(cssPath, "utf-8");
67+
rmSync(cssPath);
68+
const escaped = css.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$");
69+
js += `
70+
(async () => {
71+
if (!document.getElementById(\`${styleId}\`)) {
72+
var el = document.createElement('style');
73+
el.id = \`${styleId}\`;
74+
el.textContent = (String.raw\`${escaped}\`).trim();
75+
document.head.appendChild(el);
76+
}
77+
})();
78+
`;
79+
break;
80+
}
81+
}
82+
83+
const wrapped = `(async function() {
84+
while (!Spicetify.React || !Spicetify.ReactDOM) {
85+
await new Promise(resolve => setTimeout(resolve, 10));
86+
}
87+
${js}
88+
})();
89+
`;
90+
writeFileSync(jsOut, wrapped);
91+
}
92+
93+
async function runBuild(outDir: string): Promise<void> {
94+
if (!existsSync(outDir)) {
95+
mkdirSync(outDir, { recursive: true });
96+
}
97+
98+
const result = await Bun.build({
99+
entrypoints: [ENTRY],
100+
outdir: outDir,
101+
target: "browser",
102+
minify,
103+
naming: "[name].[ext]",
104+
root: ".",
105+
});
106+
107+
if (!result.success) {
108+
console.error("Build failed:");
109+
for (const msg of result.logs) {
110+
console.error(msg);
111+
}
112+
process.exit(1);
113+
}
114+
115+
postProcess(outDir);
116+
console.log("Build succeeded.");
117+
}
118+
119+
async function main(): Promise<void> {
120+
const outDir = await getOutDir();
121+
122+
if (watch) {
123+
await runBuild(outDir);
124+
let debounce: ReturnType<typeof setTimeout> | null = null;
125+
watchFs(SRC, { recursive: true }, () => {
126+
if (debounce) clearTimeout(debounce);
127+
debounce = setTimeout(async () => {
128+
debounce = null;
129+
await runBuild(outDir);
130+
}, 100);
131+
});
132+
console.log("Watching...");
133+
return;
134+
}
135+
136+
await runBuild(outDir);
137+
}
138+
139+
main();

bun.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"private": true,
55
"scripts": {
66
"update-types": "curl -s -o ./src/types/spicetify.d.ts https://raw.githubusercontent.com/spicetify/spicetify-cli/master/globals.d.ts",
7-
"build": "spicetify-creator",
8-
"build-local": "spicetify-creator --out=dist --minify",
9-
"watch": "spicetify-creator --watch"
7+
"build": "bun run build.ts",
8+
"build-local": "bun run build.ts --out=dist --minify",
9+
"watch": "bun run build.ts --watch"
1010
},
1111
"license": "GPL-3.0",
1212
"devDependencies": {
13+
"@types/bun": "^1.3.7",
1314
"@types/react": "^19.2.2",
1415
"@types/react-dom": "^19.2.2",
15-
"@types/wicg-file-system-access": "^2023.10.7",
16-
"spicetify-creator": "^1.0.17"
16+
"@types/wicg-file-system-access": "^2023.10.7"
1717
}
1818
}

0 commit comments

Comments
 (0)