* fix: Windows compatibility across postinstall, desktop, plugins, and scripts * fix: clean script gracefully skips locked files on Windows * test(file-secrets): add vitest coverage for xdgDataHome Exports `xdgDataHome` and adds 9 parametrized vitest cases covering: - XDG_DATA_HOME override (all platforms, with whitespace trimming) - posix $HOME/.local/share fallback + default when HOME is unset - win32 LOCALAPPDATA → APPDATA → USERPROFILE\AppData\Local chain Replaces the ad-hoc scripts/_test-windows.mjs verification script, which was never wired into CI and re-implemented the logic inline instead of testing the real code. * fix(desktop): guard registry PATH update against parse failures If `reg query HKCU\Environment /v Path` exits non-zero for any reason other than 'value not present' (exit 1), or if the output doesn't match the expected `Path REG(_EXPAND)?_SZ <value>` shape, bail out instead of writing. Previously a parse failure would silently treat current PATH as empty and overwrite it with only the executor bin dir. ---------
Showing the changes to scripts (1 of 9 files). Show all
Unified · Split
| @@ -0,0 +1,60 @@ | ||
| 1 | +#!/usr/bin/env bun | |
| 2 | +import fs from "node:fs"; | |
| 3 | +import path from "node:path"; | |
| 4 | +import { fileURLToPath } from "node:url"; | |
| 5 | + | |
| 6 | +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); | |
| 7 | + | |
| 8 | +const rootTargets = ["node_modules", ".turbo", ".astro", "dist"]; | |
| 9 | +for (const name of rootTargets) { | |
| 10 | + const p = path.join(root, name); | |
| 11 | + if (fs.existsSync(p)) { | |
| 12 | + try { | |
| 13 | + fs.rmSync(p, { recursive: true, force: true }); | |
| 14 | + console.log("removed", p); | |
| 15 | + } catch (e: any) { | |
| 16 | + console.warn("skipped (in use?):", p, "-", e.message); | |
| 17 | + } | |
| 18 | + } | |
| 19 | +} | |
| 20 | + | |
| 21 | +const nestedTargets = new Set(["node_modules", "dist", ".turbo", ".output", ".astro"]); | |
| 22 | +const nestedGlobs = /\.tsbuildinfo$/; | |
| 23 | +const searchRoots = ["apps", "packages"]; | |
| 24 | +const maxDepth = 5; | |
| 25 | + | |
| 26 | +function clean(dir: string, depth: number) { | |
| 27 | + if (depth > maxDepth) return; | |
| 28 | + let entries: fs.Dirent[]; | |
| 29 | + try { | |
| 30 | + entries = fs.readdirSync(dir, { withFileTypes: true }); | |
| 31 | + } catch { | |
| 32 | + return; | |
| 33 | + } | |
| 34 | + for (const entry of entries) { | |
| 35 | + const full = path.join(dir, entry.name); | |
| 36 | + if (entry.isDirectory()) { | |
| 37 | + if (nestedTargets.has(entry.name)) { | |
| 38 | + try { | |
| 39 | + fs.rmSync(full, { recursive: true, force: true }); | |
| 40 | + console.log("removed", full); | |
| 41 | + } catch (e: any) { | |
| 42 | + console.warn("skipped (in use?):", full, "-", e.message); | |
| 43 | + } | |
| 44 | + } else { | |
| 45 | + clean(full, depth + 1); | |
| 46 | + } | |
| 47 | + } else if (entry.isFile() && nestedGlobs.test(entry.name)) { | |
| 48 | + try { | |
| 49 | + fs.rmSync(full, { force: true }); | |
| 50 | + console.log("removed", full); | |
| 51 | + } catch (e: any) { | |
| 52 | + console.warn("skipped (in use?):", full, "-", e.message); | |
| 53 | + } | |
| 54 | + } | |
| 55 | + } | |
| 56 | +} | |
| 57 | + | |
| 58 | +for (const dir of searchRoots) { | |
| 59 | + clean(path.join(root, dir), 1); | |
| 60 | +} | |