@gjsify/cli 0.4.13 → 0.4.15
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/dist/cli.gjs.mjs +86 -78
- package/lib/actions/barrels-generate.d.ts +31 -0
- package/lib/actions/barrels-generate.js +78 -0
- package/lib/actions/build.d.ts +11 -1
- package/lib/actions/build.js +79 -3
- package/lib/bundler-pick.d.ts +7 -0
- package/lib/bundler-pick.js +17 -0
- package/lib/commands/barrels.d.ts +15 -0
- package/lib/commands/barrels.js +103 -0
- package/lib/commands/build.js +8 -0
- package/lib/commands/fix.d.ts +9 -0
- package/lib/commands/fix.js +60 -0
- package/lib/commands/flatpak/diff.d.ts +12 -0
- package/lib/commands/flatpak/diff.js +165 -0
- package/lib/commands/flatpak/index.d.ts +4 -1
- package/lib/commands/flatpak/index.js +11 -5
- package/lib/commands/flatpak/init.d.ts +1 -0
- package/lib/commands/flatpak/init.js +39 -5
- package/lib/commands/flatpak/release.d.ts +13 -0
- package/lib/commands/flatpak/release.js +152 -0
- package/lib/commands/flatpak/sync-flathub.d.ts +26 -0
- package/lib/commands/flatpak/sync-flathub.js +311 -0
- package/lib/commands/format.d.ts +12 -0
- package/lib/commands/format.js +98 -0
- package/lib/commands/index.d.ts +6 -0
- package/lib/commands/index.js +6 -0
- package/lib/commands/install.js +11 -9
- package/lib/commands/lint.d.ts +9 -0
- package/lib/commands/lint.js +60 -0
- package/lib/commands/test.d.ts +12 -0
- package/lib/commands/test.js +206 -0
- package/lib/commands/upgrade.d.ts +13 -0
- package/lib/commands/upgrade.js +402 -0
- package/lib/index.js +7 -1
- package/lib/templates/biome.json.tmpl +79 -0
- package/lib/types/cli-build-options.d.ts +7 -0
- package/lib/types/config-data.d.ts +39 -0
- package/lib/utils/biome-resolve.d.ts +47 -0
- package/lib/utils/biome-resolve.js +204 -0
- package/package.json +16 -16
- package/showcases.json +14 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// `gjsify upgrade` — drop-in replacement for `yarn upgrade-interactive`
|
|
2
|
+
// and `npx npm-check-updates`. Two modes:
|
|
3
|
+
//
|
|
4
|
+
// 1. Interactive (default): show outdated packages, prompt user to
|
|
5
|
+
// select which ones to update (space-separated indices or `a` for
|
|
6
|
+
// all), then write the new ranges to `package.json`.
|
|
7
|
+
//
|
|
8
|
+
// 2. Non-interactive (`--latest` / `--minor` / `--patch` / `--filter`):
|
|
9
|
+
// bump matching packages automatically without prompting.
|
|
10
|
+
//
|
|
11
|
+
// Workspace-aware: `workspace:^` / `workspace:~` / `workspace:*` ranges
|
|
12
|
+
// are skipped — those are the gjsify monorepo internal links and `gjsify
|
|
13
|
+
// install` resolves them locally. Only external npm specs get checked
|
|
14
|
+
// against the registry.
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
16
|
+
import { join, resolve } from "node:path";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { createInterface } from "node:readline/promises";
|
|
19
|
+
import { parse } from "@gjsify/semver";
|
|
20
|
+
import { DEFAULT_REGISTRY, fetchPackument, parseNpmrc, } from "@gjsify/npm-registry";
|
|
21
|
+
export const upgradeCommand = {
|
|
22
|
+
command: "upgrade",
|
|
23
|
+
description: "Check the npm registry for newer versions of declared dependencies and update package.json. Interactive by default; `--latest` / `--minor` / `--patch` switch to non-interactive bulk-update mode.",
|
|
24
|
+
builder: (yargs) => {
|
|
25
|
+
return yargs
|
|
26
|
+
.option("latest", {
|
|
27
|
+
description: "Non-interactive: bump every dependency to its latest version (allows major).",
|
|
28
|
+
type: "boolean",
|
|
29
|
+
default: false,
|
|
30
|
+
})
|
|
31
|
+
.option("minor", {
|
|
32
|
+
description: "Non-interactive: bump every dependency to the latest within the same major (semver-minor + semver-patch).",
|
|
33
|
+
type: "boolean",
|
|
34
|
+
default: false,
|
|
35
|
+
})
|
|
36
|
+
.option("patch", {
|
|
37
|
+
description: "Non-interactive: bump every dependency to the latest within the same minor (semver-patch only).",
|
|
38
|
+
type: "boolean",
|
|
39
|
+
default: false,
|
|
40
|
+
})
|
|
41
|
+
.option("filter", {
|
|
42
|
+
description: "Only consider packages whose name matches this substring (case-insensitive). Repeatable; comma-separated values are split.",
|
|
43
|
+
type: "string",
|
|
44
|
+
})
|
|
45
|
+
.option("dry-run", {
|
|
46
|
+
description: "Print the upgrade plan without writing package.json.",
|
|
47
|
+
type: "boolean",
|
|
48
|
+
default: false,
|
|
49
|
+
})
|
|
50
|
+
.option("cwd", {
|
|
51
|
+
description: "Project directory. Default: process.cwd().",
|
|
52
|
+
type: "string",
|
|
53
|
+
})
|
|
54
|
+
.option("yes", {
|
|
55
|
+
alias: "y",
|
|
56
|
+
description: "Interactive mode: select all without prompting.",
|
|
57
|
+
type: "boolean",
|
|
58
|
+
default: false,
|
|
59
|
+
})
|
|
60
|
+
.option("verbose", {
|
|
61
|
+
description: "Print extra resolution details.",
|
|
62
|
+
type: "boolean",
|
|
63
|
+
default: false,
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
handler: async (args) => {
|
|
67
|
+
const cwd = resolve(args.cwd ?? process.cwd());
|
|
68
|
+
const pkgJsonPath = join(cwd, "package.json");
|
|
69
|
+
if (!existsSync(pkgJsonPath)) {
|
|
70
|
+
throw new Error(`[gjsify upgrade] no package.json at ${pkgJsonPath}`);
|
|
71
|
+
}
|
|
72
|
+
const rawPkg = readFileSync(pkgJsonPath, "utf-8");
|
|
73
|
+
const pkg = JSON.parse(rawPkg);
|
|
74
|
+
const filters = args.filter
|
|
75
|
+
? args.filter
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((s) => s.trim().toLowerCase())
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
: [];
|
|
80
|
+
const entries = collectExternalDeps(pkg, filters);
|
|
81
|
+
if (entries.length === 0) {
|
|
82
|
+
console.log("[gjsify upgrade] no external npm dependencies to check.");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const npmrc = await loadNpmrcLight(cwd);
|
|
86
|
+
const mode = args.latest
|
|
87
|
+
? "latest"
|
|
88
|
+
: args.minor
|
|
89
|
+
? "minor"
|
|
90
|
+
: args.patch
|
|
91
|
+
? "patch"
|
|
92
|
+
: "interactive";
|
|
93
|
+
console.log(`[gjsify upgrade] checking ${entries.length} dependencies against ${npmrc.registry}…`);
|
|
94
|
+
const candidates = await resolveCandidates(entries, npmrc, args.verbose ?? false, mode);
|
|
95
|
+
if (candidates.length === 0) {
|
|
96
|
+
console.log("✅ all dependencies are up to date");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
printTable(candidates);
|
|
100
|
+
let selected;
|
|
101
|
+
if (mode === "interactive" && !args.yes) {
|
|
102
|
+
selected = await promptSelection(candidates);
|
|
103
|
+
}
|
|
104
|
+
else if (args.yes && mode === "interactive") {
|
|
105
|
+
console.log("[gjsify upgrade] -y / --yes: selecting all");
|
|
106
|
+
selected = candidates;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
selected = candidates;
|
|
110
|
+
}
|
|
111
|
+
if (selected.length === 0) {
|
|
112
|
+
console.log("[gjsify upgrade] nothing selected; package.json unchanged.");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (args.dryRun) {
|
|
116
|
+
console.log(`[gjsify upgrade] --dry-run: would update ${selected.length} dependencies (no write).`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
writePackageJson(pkgJsonPath, rawPkg, pkg, selected);
|
|
120
|
+
console.log(`✏️ updated ${selected.length} dependencies in ${pkgJsonPath}. Run \`gjsify install\` to apply.`);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
// ─── Resolution ─────────────────────────────────────────────────────────
|
|
124
|
+
const DEP_FIELDS = [
|
|
125
|
+
"dependencies",
|
|
126
|
+
"devDependencies",
|
|
127
|
+
"optionalDependencies",
|
|
128
|
+
"peerDependencies",
|
|
129
|
+
];
|
|
130
|
+
function collectExternalDeps(pkg, filters) {
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const field of DEP_FIELDS) {
|
|
133
|
+
const map = pkg[field];
|
|
134
|
+
if (!map || typeof map !== "object")
|
|
135
|
+
continue;
|
|
136
|
+
for (const [name, raw] of Object.entries(map)) {
|
|
137
|
+
if (typeof raw !== "string")
|
|
138
|
+
continue;
|
|
139
|
+
if (filters.length && !filters.some((f) => name.toLowerCase().includes(f))) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
// Skip workspace-protocol + file: + link: + git: + http(s): specs.
|
|
143
|
+
if (raw.startsWith("workspace:") ||
|
|
144
|
+
raw.startsWith("file:") ||
|
|
145
|
+
raw.startsWith("link:") ||
|
|
146
|
+
raw.startsWith("git+") ||
|
|
147
|
+
raw.startsWith("git:") ||
|
|
148
|
+
raw.startsWith("http") ||
|
|
149
|
+
raw.startsWith("npm:") || // e.g. `foo: npm:@scope/foo@^1`
|
|
150
|
+
raw === "*" ||
|
|
151
|
+
raw === "latest") {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const { prefix, version } = splitRange(raw);
|
|
155
|
+
out.push({
|
|
156
|
+
name,
|
|
157
|
+
field,
|
|
158
|
+
currentRange: raw,
|
|
159
|
+
currentVersion: version,
|
|
160
|
+
prefix,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return out;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Split `^1.2.3` → { prefix: "^", version: "1.2.3" }. Honors `~`, `>=`,
|
|
168
|
+
* `>`, `<=`, `<`, `=`. Defaults to "" prefix when the range is just a
|
|
169
|
+
* literal version.
|
|
170
|
+
*/
|
|
171
|
+
function splitRange(range) {
|
|
172
|
+
const m = range.match(/^(\^|~|>=|<=|>|<|=)?\s*([0-9].*)$/);
|
|
173
|
+
if (!m)
|
|
174
|
+
return { prefix: "", version: null };
|
|
175
|
+
const prefix = m[1] ?? "";
|
|
176
|
+
const version = m[2]?.split(/\s|[|&,]/)[0] ?? null; // strip range modifiers (`||`, ` - `, etc.)
|
|
177
|
+
const parsed = version ? parse(version) : null;
|
|
178
|
+
return { prefix, version: parsed?.version ?? null };
|
|
179
|
+
}
|
|
180
|
+
async function resolveCandidates(entries, npmrc, verbose, mode) {
|
|
181
|
+
const results = [];
|
|
182
|
+
// Parallel fetch with a small concurrency cap.
|
|
183
|
+
const cap = 8;
|
|
184
|
+
let cursor = 0;
|
|
185
|
+
async function worker() {
|
|
186
|
+
for (;;) {
|
|
187
|
+
const i = cursor++;
|
|
188
|
+
if (i >= entries.length)
|
|
189
|
+
return;
|
|
190
|
+
const entry = entries[i];
|
|
191
|
+
try {
|
|
192
|
+
const packument = await fetchPackument(entry.name, { npmrc });
|
|
193
|
+
const latest = packument["dist-tags"]?.latest;
|
|
194
|
+
if (!latest) {
|
|
195
|
+
if (verbose)
|
|
196
|
+
console.warn(` ${entry.name}: no dist-tags.latest, skipping`);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!entry.currentVersion) {
|
|
200
|
+
if (verbose)
|
|
201
|
+
console.warn(` ${entry.name}: unable to parse current range "${entry.currentRange}"`);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const diff = classifyDiff(entry.currentVersion, latest);
|
|
205
|
+
if (diff === "none")
|
|
206
|
+
continue;
|
|
207
|
+
if (mode === "minor" && diff === "major")
|
|
208
|
+
continue;
|
|
209
|
+
if (mode === "patch" && (diff === "major" || diff === "minor"))
|
|
210
|
+
continue;
|
|
211
|
+
results.push({
|
|
212
|
+
...entry,
|
|
213
|
+
latestVersion: latest,
|
|
214
|
+
diff,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
if (verbose)
|
|
219
|
+
console.warn(` ${entry.name}: fetch failed (${err.message})`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
void packumentToString;
|
|
223
|
+
}
|
|
224
|
+
await Promise.all(Array.from({ length: cap }, () => worker()));
|
|
225
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
226
|
+
return results;
|
|
227
|
+
}
|
|
228
|
+
function classifyDiff(current, latest) {
|
|
229
|
+
const c = parse(current);
|
|
230
|
+
const l = parse(latest);
|
|
231
|
+
if (!c || !l)
|
|
232
|
+
return "none";
|
|
233
|
+
if (c.major !== l.major)
|
|
234
|
+
return l.major > c.major ? "major" : "none";
|
|
235
|
+
if (c.minor !== l.minor)
|
|
236
|
+
return l.minor > c.minor ? "minor" : "none";
|
|
237
|
+
if (c.patch !== l.patch)
|
|
238
|
+
return l.patch > c.patch ? "patch" : "none";
|
|
239
|
+
if ((c.prerelease ?? []).join(".") !== (l.prerelease ?? []).join("."))
|
|
240
|
+
return "prerelease";
|
|
241
|
+
return "none";
|
|
242
|
+
}
|
|
243
|
+
// ─── Output / Interaction ──────────────────────────────────────────────
|
|
244
|
+
const ANSI = {
|
|
245
|
+
reset: "\x1b[0m",
|
|
246
|
+
bold: "\x1b[1m",
|
|
247
|
+
dim: "\x1b[2m",
|
|
248
|
+
red: "\x1b[31m",
|
|
249
|
+
yellow: "\x1b[33m",
|
|
250
|
+
green: "\x1b[32m",
|
|
251
|
+
cyan: "\x1b[36m",
|
|
252
|
+
};
|
|
253
|
+
function colorForDiff(diff) {
|
|
254
|
+
switch (diff) {
|
|
255
|
+
case "major":
|
|
256
|
+
return ANSI.red;
|
|
257
|
+
case "minor":
|
|
258
|
+
return ANSI.yellow;
|
|
259
|
+
case "patch":
|
|
260
|
+
return ANSI.green;
|
|
261
|
+
case "prerelease":
|
|
262
|
+
return ANSI.cyan;
|
|
263
|
+
default:
|
|
264
|
+
return "";
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function printTable(candidates) {
|
|
268
|
+
const nameW = Math.max(...candidates.map((c) => c.name.length), 4);
|
|
269
|
+
const curW = Math.max(...candidates.map((c) => c.currentRange.length), 7);
|
|
270
|
+
const newW = Math.max(...candidates.map((c) => c.latestVersion.length), 6);
|
|
271
|
+
const idxW = String(candidates.length).length + 2;
|
|
272
|
+
const head = " ".repeat(idxW) +
|
|
273
|
+
ANSI.bold +
|
|
274
|
+
"name".padEnd(nameW) +
|
|
275
|
+
" " +
|
|
276
|
+
"current".padEnd(curW) +
|
|
277
|
+
" " +
|
|
278
|
+
"latest".padEnd(newW) +
|
|
279
|
+
" " +
|
|
280
|
+
"kind" +
|
|
281
|
+
ANSI.reset;
|
|
282
|
+
console.log(head);
|
|
283
|
+
console.log(" ".repeat(idxW) + ANSI.dim + "─".repeat(nameW + curW + newW + 12) + ANSI.reset);
|
|
284
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
285
|
+
const c = candidates[i];
|
|
286
|
+
const idx = `${i + 1}.`.padEnd(idxW);
|
|
287
|
+
const color = colorForDiff(c.diff);
|
|
288
|
+
console.log(idx +
|
|
289
|
+
c.name.padEnd(nameW) +
|
|
290
|
+
" " +
|
|
291
|
+
ANSI.dim +
|
|
292
|
+
c.currentRange.padEnd(curW) +
|
|
293
|
+
ANSI.reset +
|
|
294
|
+
" " +
|
|
295
|
+
color +
|
|
296
|
+
c.latestVersion.padEnd(newW) +
|
|
297
|
+
ANSI.reset +
|
|
298
|
+
" " +
|
|
299
|
+
color +
|
|
300
|
+
c.diff +
|
|
301
|
+
ANSI.reset);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async function promptSelection(candidates) {
|
|
305
|
+
if (!process.stdin.isTTY) {
|
|
306
|
+
console.log("[gjsify upgrade] non-TTY stdin: pass --latest / --minor / --patch (or --yes for interactive-all) to upgrade non-interactively.");
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
309
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
310
|
+
try {
|
|
311
|
+
console.log("\nSelect upgrades: comma- or space-separated indices, " +
|
|
312
|
+
ANSI.bold +
|
|
313
|
+
"a" +
|
|
314
|
+
ANSI.reset +
|
|
315
|
+
" for all, ranges like " +
|
|
316
|
+
ANSI.bold +
|
|
317
|
+
"1-3" +
|
|
318
|
+
ANSI.reset +
|
|
319
|
+
", or " +
|
|
320
|
+
ANSI.bold +
|
|
321
|
+
"ENTER" +
|
|
322
|
+
ANSI.reset +
|
|
323
|
+
" to skip:");
|
|
324
|
+
const answer = (await rl.question("> ")).trim();
|
|
325
|
+
if (!answer)
|
|
326
|
+
return [];
|
|
327
|
+
if (answer.toLowerCase() === "a" || answer.toLowerCase() === "all")
|
|
328
|
+
return candidates;
|
|
329
|
+
const picked = new Set();
|
|
330
|
+
for (const token of answer.split(/[\s,]+/).filter(Boolean)) {
|
|
331
|
+
const range = token.match(/^(\d+)-(\d+)$/);
|
|
332
|
+
if (range) {
|
|
333
|
+
const a = Number(range[1]);
|
|
334
|
+
const b = Number(range[2]);
|
|
335
|
+
for (let i = Math.min(a, b); i <= Math.max(a, b); i++)
|
|
336
|
+
picked.add(i - 1);
|
|
337
|
+
}
|
|
338
|
+
else if (/^\d+$/.test(token)) {
|
|
339
|
+
picked.add(Number(token) - 1);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return [...picked]
|
|
343
|
+
.filter((i) => i >= 0 && i < candidates.length)
|
|
344
|
+
.map((i) => candidates[i]);
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
rl.close();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ─── Write-back ────────────────────────────────────────────────────────
|
|
351
|
+
function writePackageJson(path, rawText, parsed, selected) {
|
|
352
|
+
// Mutate the parsed object then re-stringify with the original indent.
|
|
353
|
+
for (const c of selected) {
|
|
354
|
+
const map = parsed[c.field];
|
|
355
|
+
if (!map)
|
|
356
|
+
continue;
|
|
357
|
+
map[c.name] = c.prefix + c.latestVersion;
|
|
358
|
+
}
|
|
359
|
+
const indent = detectIndent(rawText);
|
|
360
|
+
writeFileSync(path, JSON.stringify(parsed, null, indent) + (rawText.endsWith("\n") ? "\n" : ""), "utf-8");
|
|
361
|
+
}
|
|
362
|
+
function detectIndent(json) {
|
|
363
|
+
const m = json.match(/^\{\n( +)/);
|
|
364
|
+
if (m)
|
|
365
|
+
return m[1].length;
|
|
366
|
+
return 2;
|
|
367
|
+
}
|
|
368
|
+
// ─── npmrc loader (lightweight, shared shape with install-backend) ─────
|
|
369
|
+
async function loadNpmrcLight(cwd) {
|
|
370
|
+
let parsed = {
|
|
371
|
+
registry: DEFAULT_REGISTRY,
|
|
372
|
+
scopes: {},
|
|
373
|
+
authTokens: {},
|
|
374
|
+
basicAuth: {},
|
|
375
|
+
};
|
|
376
|
+
// Layered .npmrc lookup (most-specific wins): home → cwd. Same precedence
|
|
377
|
+
// as install-backend-native, except env-var `npm_config_registry` wins
|
|
378
|
+
// over file values (matches npm's real semantics, lets the test harness
|
|
379
|
+
// point at a mock registry without touching `~/.npmrc`).
|
|
380
|
+
for (const candidate of [join(homedir(), ".npmrc"), join(cwd, ".npmrc")]) {
|
|
381
|
+
if (!existsSync(candidate))
|
|
382
|
+
continue;
|
|
383
|
+
try {
|
|
384
|
+
const proj = parseNpmrc(readFileSync(candidate, "utf-8"));
|
|
385
|
+
parsed = {
|
|
386
|
+
...parsed,
|
|
387
|
+
...proj,
|
|
388
|
+
scopes: { ...parsed.scopes, ...proj.scopes },
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
// ignore malformed .npmrc — same lenient policy as install-backend
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (process.env.npm_config_registry) {
|
|
396
|
+
parsed.registry = process.env.npm_config_registry;
|
|
397
|
+
}
|
|
398
|
+
return parsed;
|
|
399
|
+
}
|
|
400
|
+
function packumentToString(p) {
|
|
401
|
+
return `${p.name}@${p["dist-tags"]?.latest ?? "?"}`;
|
|
402
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import yargs from 'yargs';
|
|
3
3
|
import { hideBin } from 'yargs/helpers';
|
|
4
|
-
import { buildCommand as build, runCommand as run, infoCommand as info, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, } from './commands/index.js';
|
|
4
|
+
import { buildCommand as build, testCommand as test, runCommand as run, infoCommand as info, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, } from './commands/index.js';
|
|
5
5
|
import { APP_NAME } from './constants.js';
|
|
6
6
|
// `parseAsync()` instead of `.argv` so the top-level await keeps the
|
|
7
7
|
// process alive until command handlers complete. Under Node this is
|
|
@@ -14,6 +14,7 @@ await yargs(hideBin(process.argv))
|
|
|
14
14
|
.command(create.command, create.description, create.builder, create.handler)
|
|
15
15
|
.command(install.command, install.description, install.builder, install.handler)
|
|
16
16
|
.command(build.command, build.description, build.builder, build.handler)
|
|
17
|
+
.command(test.command, test.description, test.builder, test.handler)
|
|
17
18
|
.command(run.command, run.description, run.builder, run.handler)
|
|
18
19
|
.command(dlx.command, dlx.description, dlx.builder, dlx.handler)
|
|
19
20
|
.command(info.command, info.description, info.builder, info.handler)
|
|
@@ -30,6 +31,11 @@ await yargs(hideBin(process.argv))
|
|
|
30
31
|
.command(selfUpdate.command, selfUpdate.description, selfUpdate.builder, selfUpdate.handler)
|
|
31
32
|
.command(generateInstaller.command, generateInstaller.description, generateInstaller.builder, generateInstaller.handler)
|
|
32
33
|
.command(uninstall.command, uninstall.description, uninstall.builder, uninstall.handler)
|
|
34
|
+
.command(upgrade.command, upgrade.description, upgrade.builder, upgrade.handler)
|
|
35
|
+
.command(format.command, format.description, format.builder, format.handler)
|
|
36
|
+
.command(lint.command, lint.description, lint.builder, lint.handler)
|
|
37
|
+
.command(fix.command, fix.description, fix.builder, fix.handler)
|
|
38
|
+
.command(barrels.command, barrels.description, barrels.builder, barrels.handler)
|
|
33
39
|
.demandCommand(1)
|
|
34
40
|
.help()
|
|
35
41
|
.parseAsync();
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.13/schema.json",
|
|
3
|
+
"formatter": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"indentStyle": "space",
|
|
6
|
+
"indentWidth": 4,
|
|
7
|
+
"lineWidth": 120,
|
|
8
|
+
"lineEnding": "lf"
|
|
9
|
+
},
|
|
10
|
+
"linter": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"rules": {
|
|
13
|
+
"recommended": true,
|
|
14
|
+
"style": {
|
|
15
|
+
"useImportType": "warn",
|
|
16
|
+
"useNodejsImportProtocol": "error",
|
|
17
|
+
"noNonNullAssertion": "off"
|
|
18
|
+
},
|
|
19
|
+
"suspicious": {
|
|
20
|
+
"noExplicitAny": "warn",
|
|
21
|
+
"noConsole": "off"
|
|
22
|
+
},
|
|
23
|
+
"correctness": {
|
|
24
|
+
"noUnusedImports": "warn"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"javascript": {
|
|
29
|
+
"formatter": {
|
|
30
|
+
"quoteStyle": "single",
|
|
31
|
+
"jsxQuoteStyle": "double",
|
|
32
|
+
"semicolons": "always",
|
|
33
|
+
"trailingCommas": "all",
|
|
34
|
+
"arrowParentheses": "always",
|
|
35
|
+
"bracketSpacing": true,
|
|
36
|
+
"bracketSameLine": false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"json": {
|
|
40
|
+
"formatter": {
|
|
41
|
+
"indentWidth": 2,
|
|
42
|
+
"trailingCommas": "none",
|
|
43
|
+
"lineWidth": 120
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"css": {
|
|
47
|
+
"formatter": {
|
|
48
|
+
"indentWidth": 2,
|
|
49
|
+
"quoteStyle": "single"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"files": {
|
|
53
|
+
"includes": [
|
|
54
|
+
"**",
|
|
55
|
+
"!**/node_modules",
|
|
56
|
+
"!**/dist",
|
|
57
|
+
"!**/lib",
|
|
58
|
+
"!**/build",
|
|
59
|
+
"!**/build-dir",
|
|
60
|
+
"!**/builddir",
|
|
61
|
+
"!**/flatpak-build",
|
|
62
|
+
"!**/.flatpak-builder",
|
|
63
|
+
"!**/repo",
|
|
64
|
+
"!**/coverage",
|
|
65
|
+
"!**/refs",
|
|
66
|
+
"!**/@types",
|
|
67
|
+
"!**/templates",
|
|
68
|
+
"!**/prebuilds",
|
|
69
|
+
"!**/.yarn/cache",
|
|
70
|
+
"!**/.yarn/install-state.gz",
|
|
71
|
+
"!**/cli.gjs.mjs",
|
|
72
|
+
"!**/test.gjs.mjs",
|
|
73
|
+
"!**/test.node.mjs",
|
|
74
|
+
"!**/*.gresource",
|
|
75
|
+
"!**/*.compiled",
|
|
76
|
+
"!**/*.metainfo.xml"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -96,4 +96,11 @@ export interface CliBuildOptions {
|
|
|
96
96
|
* Example: `--exclude-globals fetch,XMLHttpRequest`
|
|
97
97
|
*/
|
|
98
98
|
excludeGlobals?: string[];
|
|
99
|
+
/**
|
|
100
|
+
* Watch source files and rebuild on change. Logs each rebuild with
|
|
101
|
+
* duration; SIGINT/SIGTERM cleanly closes the watcher. Only valid with
|
|
102
|
+
* `--app gjs|node|browser` (rejected with `--library`). Requires the
|
|
103
|
+
* npm `rolldown` engine — run under Node.
|
|
104
|
+
*/
|
|
105
|
+
watch?: boolean;
|
|
99
106
|
}
|
|
@@ -186,6 +186,38 @@ export interface ConfigData {
|
|
|
186
186
|
* declaratively. CLI flags override these values.
|
|
187
187
|
*/
|
|
188
188
|
flatpak?: ConfigDataFlatpak;
|
|
189
|
+
/**
|
|
190
|
+
* Format/lint config consumed by `gjsify format` / `gjsify lint` /
|
|
191
|
+
* `gjsify fix`. Thin shell — Biome's own `biome.json` is the real
|
|
192
|
+
* configuration file; we only need a pointer here.
|
|
193
|
+
*/
|
|
194
|
+
format?: ConfigDataFormat;
|
|
195
|
+
/**
|
|
196
|
+
* Test-runner configuration consumed by `gjsify test`. CLI flags
|
|
197
|
+
* (--entry, --outdir, --runtime) override these values.
|
|
198
|
+
*/
|
|
199
|
+
test?: ConfigDataTest;
|
|
200
|
+
}
|
|
201
|
+
/** Optional pointer to a non-default biome.json. */
|
|
202
|
+
export interface ConfigDataFormat {
|
|
203
|
+
/**
|
|
204
|
+
* Path to biome.json. Default: walks up from cwd to find one;
|
|
205
|
+
* falls back to the recommended template shipped with `gjsify`
|
|
206
|
+
* (writable via `gjsify format --init`).
|
|
207
|
+
*/
|
|
208
|
+
configPath?: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* `gjsify test` configuration. All fields optional — sensible defaults
|
|
212
|
+
* apply when missing.
|
|
213
|
+
*/
|
|
214
|
+
export interface ConfigDataTest {
|
|
215
|
+
/** Path to the test entry. Default: `src/test.mts`. */
|
|
216
|
+
entry?: string;
|
|
217
|
+
/** Output directory for the built test bundles. Default: `dist/`. */
|
|
218
|
+
outdir?: string;
|
|
219
|
+
/** Default runtimes when `--runtime` not specified. Default: `['gjs', 'node']`. */
|
|
220
|
+
runtimes?: Array<'gjs' | 'node'>;
|
|
189
221
|
}
|
|
190
222
|
/**
|
|
191
223
|
* Flatpak-toolchain config consumed by the `gjsify flatpak` subcommand
|
|
@@ -417,6 +449,13 @@ export interface ConfigDataFlatpak {
|
|
|
417
449
|
displayLengthMin?: number;
|
|
418
450
|
controls?: Array<'keyboard' | 'pointing' | 'touch' | 'gamepad' | 'tablet' | 'console'>;
|
|
419
451
|
};
|
|
452
|
+
/**
|
|
453
|
+
* Flathub tracking-repo override for `gjsify flatpak sync-flathub` /
|
|
454
|
+
* `gjsify flatpak diff`. Default: `flathub/<appId>` (e.g.
|
|
455
|
+
* `flathub/eu.jumplink.Learn6502`). Set this when the upstream repo
|
|
456
|
+
* deviates from that convention.
|
|
457
|
+
*/
|
|
458
|
+
flathubRepo?: string;
|
|
420
459
|
}
|
|
421
460
|
/**
|
|
422
461
|
* A single block inside a MetaInfo `<description>`. Either a paragraph
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the absolute path to the biome native binary for the current
|
|
3
|
+
* platform. Walks cwd → workspace-root looking for the matching
|
|
4
|
+
* `@biomejs/cli-<platform>-<arch>` package.
|
|
5
|
+
*
|
|
6
|
+
* Throws with a clear install-hint when not found.
|
|
7
|
+
*/
|
|
8
|
+
export declare function findBiomeBin(cwd?: string): string;
|
|
9
|
+
export declare class BiomeNotFoundError extends Error {
|
|
10
|
+
pkg: string;
|
|
11
|
+
cwd: string;
|
|
12
|
+
constructor(pkg: string, cwd: string);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Walk up from a starting directory to find the nearest `biome.json` or
|
|
16
|
+
* `biome.jsonc`. Returns absolute path or null. Workspace-aware: also
|
|
17
|
+
* probes the workspace root.
|
|
18
|
+
*/
|
|
19
|
+
export declare function findBiomeConfig(cwd?: string): string | null;
|
|
20
|
+
export interface RunBiomeOptions {
|
|
21
|
+
cwd?: string;
|
|
22
|
+
verbose?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Spawn biome with the given args. Inherits stdio so biome's own output
|
|
26
|
+
* (formatted source, lint diagnostics, summary lines) reaches the user.
|
|
27
|
+
*
|
|
28
|
+
* Returns the exit code as a number; never throws on non-zero exit
|
|
29
|
+
* (callers check the code).
|
|
30
|
+
*/
|
|
31
|
+
export declare function runBiome(args: string[], opts?: RunBiomeOptions): Promise<number>;
|
|
32
|
+
/**
|
|
33
|
+
* Lazy-load the embedded `biome.json.tmpl` content. The static-read-inliner
|
|
34
|
+
* matches this readFileSync(new URL(<lit>, import.meta.url), 'utf-8')
|
|
35
|
+
* shape at build time and inlines the file into the GJS bundle, so the
|
|
36
|
+
* template is available without runtime file I/O against the install dir.
|
|
37
|
+
*/
|
|
38
|
+
export declare function loadBiomeTemplate(): string;
|
|
39
|
+
/** Helper for callers to surface the install hint to the user cleanly. */
|
|
40
|
+
export declare function printBiomeNotFound(err: BiomeNotFoundError): void;
|
|
41
|
+
/**
|
|
42
|
+
* Has `@biomejs/biome` (the npm wrapper package) been declared in the
|
|
43
|
+
* project's `package.json` devDependencies or dependencies? Useful as a
|
|
44
|
+
* cheap pre-flight check — `gjsify flatpak init`'s G.2 post-format hook
|
|
45
|
+
* uses this to decide whether to auto-format its outputs.
|
|
46
|
+
*/
|
|
47
|
+
export declare function hasBiomeDevDep(cwd?: string): boolean;
|