@gjsify/cli 0.4.11 → 0.4.13
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 +16 -13
- package/lib/commands/flatpak/check.d.ts +11 -0
- package/lib/commands/flatpak/check.js +163 -0
- package/lib/commands/flatpak/index.d.ts +2 -1
- package/lib/commands/flatpak/index.js +5 -3
- package/lib/commands/flatpak/init.d.ts +4 -0
- package/lib/commands/flatpak/init.js +94 -27
- package/lib/commands/flatpak/scaffold.d.ts +26 -0
- package/lib/commands/flatpak/scaffold.js +327 -0
- package/lib/templates/flatpak/desktop.tmpl +10 -0
- package/lib/templates/flatpak/flathub-app.json.tmpl +1 -0
- package/lib/templates/flatpak/flathub-cli.json.tmpl +3 -0
- package/lib/types/config-data.d.ts +201 -0
- package/package.json +16 -16
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// MetaInfo XML / .desktop / flathub.json scaffolding for
|
|
2
|
+
// `gjsify flatpak init`. Phase F.9.6 onwards builds the MetaInfo XML
|
|
3
|
+
// directly in TypeScript instead of substituting into a static template —
|
|
4
|
+
// the AppStream surface (description blocks, per-release rich notes,
|
|
5
|
+
// translator hints, kudos, supports/requires/recommends, content_rating
|
|
6
|
+
// attributes, provides) has too many optional nested sections for a
|
|
7
|
+
// template+placeholder approach to stay legible.
|
|
8
|
+
//
|
|
9
|
+
// `.desktop` and `flathub.json` keep their static templates (they're
|
|
10
|
+
// flat key=value or empty-object files where substitution is fine).
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
/**
|
|
13
|
+
* Lazy template loaders for the two artefacts that stay template-based.
|
|
14
|
+
* `static-read-inliner` matches this shape and inlines the templates
|
|
15
|
+
* into the GJS bundle at build time.
|
|
16
|
+
*/
|
|
17
|
+
function loadDesktopTemplate() {
|
|
18
|
+
return readFileSync(new URL('../../templates/flatpak/desktop.tmpl', import.meta.url), 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
function loadFlathubAppTemplate() {
|
|
21
|
+
return readFileSync(new URL('../../templates/flatpak/flathub-app.json.tmpl', import.meta.url), 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
function loadFlathubCliTemplate() {
|
|
24
|
+
return readFileSync(new URL('../../templates/flatpak/flathub-cli.json.tmpl', import.meta.url), 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate that the config has the minimum set of fields required for
|
|
28
|
+
* MetaInfo XML rendering. Returns the list of missing fields with
|
|
29
|
+
* actionable hints; empty list means OK.
|
|
30
|
+
*/
|
|
31
|
+
export function validateScaffoldInputs(inputs) {
|
|
32
|
+
const f = inputs.flatpak;
|
|
33
|
+
const missing = [];
|
|
34
|
+
if (!f.developer?.id || !f.developer?.name) {
|
|
35
|
+
missing.push({
|
|
36
|
+
field: 'gjsify.flatpak.developer',
|
|
37
|
+
hint: 'Set `gjsify.flatpak.developer = { "id": "io.github.you", "name": "Your Name" }` in package.json. The id is reverse-DNS.',
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (!f.summary) {
|
|
41
|
+
missing.push({
|
|
42
|
+
field: 'gjsify.flatpak.summary',
|
|
43
|
+
hint: 'One-line app summary, ≤80 chars, no trailing period. Example: "Learn 6502 assembly language".',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (!f.description) {
|
|
47
|
+
missing.push({
|
|
48
|
+
field: 'gjsify.flatpak.description',
|
|
49
|
+
hint: 'Plain text (split on blank lines) or DescriptionBlock[] for rich content with bullet lists + translator hints.',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!f.license?.project) {
|
|
53
|
+
missing.push({
|
|
54
|
+
field: 'gjsify.flatpak.license.project',
|
|
55
|
+
hint: 'SPDX identifier of the project license, e.g. "MIT", "GPL-3.0-or-later".',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (!f.homepageUrl) {
|
|
59
|
+
missing.push({
|
|
60
|
+
field: 'gjsify.flatpak.homepageUrl',
|
|
61
|
+
hint: 'Required by Flathub. Example: "https://github.com/you/your-repo".',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return missing;
|
|
65
|
+
}
|
|
66
|
+
/** Render the MetaInfo XML for a desktop application. */
|
|
67
|
+
export function renderMetainfoApp(inputs) {
|
|
68
|
+
return renderMetainfo(inputs, 'desktop-application');
|
|
69
|
+
}
|
|
70
|
+
/** Render the MetaInfo XML for a console application. */
|
|
71
|
+
export function renderMetainfoCli(inputs) {
|
|
72
|
+
return renderMetainfo(inputs, 'console-application');
|
|
73
|
+
}
|
|
74
|
+
/** Render the .desktop entry (app kind only). */
|
|
75
|
+
export function renderDesktop(inputs) {
|
|
76
|
+
const f = inputs.flatpak;
|
|
77
|
+
const categoriesLine = (f.categories ?? ['Utility']).join(';') + ';';
|
|
78
|
+
const keywordsLine = f.keywords?.length
|
|
79
|
+
? `Keywords=${f.keywords.join(';')};\n`
|
|
80
|
+
: '';
|
|
81
|
+
return substitute(loadDesktopTemplate(), {
|
|
82
|
+
NAME: inputs.name,
|
|
83
|
+
SUMMARY: f.summary ?? inputs.name,
|
|
84
|
+
COMMAND: inputs.command,
|
|
85
|
+
APP_ID: inputs.appId,
|
|
86
|
+
CATEGORIES_LINE: categoriesLine,
|
|
87
|
+
KEYWORDS_LINE: keywordsLine,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** Render the flathub.json policy file. */
|
|
91
|
+
export function renderFlathubJson(kind) {
|
|
92
|
+
return kind === 'cli' ? loadFlathubCliTemplate() : loadFlathubAppTemplate();
|
|
93
|
+
}
|
|
94
|
+
// ─── MetaInfo XML builder ────────────────────────────────────────────────
|
|
95
|
+
function renderMetainfo(inputs, kind) {
|
|
96
|
+
const f = inputs.flatpak;
|
|
97
|
+
const year = new Date().getFullYear();
|
|
98
|
+
const developerName = f.developer?.name ?? '';
|
|
99
|
+
const lines = [];
|
|
100
|
+
lines.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
101
|
+
lines.push(`<!-- Copyright ${year} ${escapeXml(developerName)} -->`);
|
|
102
|
+
lines.push(`<component type="${kind}">`);
|
|
103
|
+
lines.push(` <id>${escapeXml(inputs.appId)}</id>`);
|
|
104
|
+
lines.push(` <metadata_license>${escapeXml(f.license?.metadata ?? 'CC0-1.0')}</metadata_license>`);
|
|
105
|
+
lines.push(` <project_license>${escapeXml(f.license?.project ?? '')}</project_license>`);
|
|
106
|
+
lines.push(` <name>${escapeXml(inputs.name)}</name>`);
|
|
107
|
+
pushTranslatorHint(lines, f.summaryTranslatorHint, ' ');
|
|
108
|
+
lines.push(` <summary>${escapeXml(f.summary ?? inputs.name)}</summary>`);
|
|
109
|
+
if (f.iconRemote) {
|
|
110
|
+
lines.push(` <icon type="remote">${escapeXml(f.iconRemote)}</icon>`);
|
|
111
|
+
}
|
|
112
|
+
// <description>
|
|
113
|
+
lines.push(' <description>');
|
|
114
|
+
for (const blockLine of renderDescriptionBlocks(f.description ?? '', ' ')) {
|
|
115
|
+
lines.push(blockLine);
|
|
116
|
+
}
|
|
117
|
+
lines.push(' </description>');
|
|
118
|
+
// <developer>
|
|
119
|
+
if (f.developer?.id && f.developer?.name) {
|
|
120
|
+
lines.push(` <developer id="${escapeXml(f.developer.id)}">`);
|
|
121
|
+
const translateAttr = f.developer.nameTranslatable === true ? '' : ' translate="no"';
|
|
122
|
+
lines.push(` <name${translateAttr}>${escapeXml(f.developer.name)}</name>`);
|
|
123
|
+
if (f.developer.email) {
|
|
124
|
+
lines.push(` <email>${escapeXml(f.developer.email)}</email>`);
|
|
125
|
+
}
|
|
126
|
+
lines.push(' </developer>');
|
|
127
|
+
}
|
|
128
|
+
if (kind === 'desktop-application') {
|
|
129
|
+
lines.push(` <launchable type="desktop-id">${escapeXml(inputs.appId)}.desktop</launchable>`);
|
|
130
|
+
}
|
|
131
|
+
// <screenshots>
|
|
132
|
+
if (f.screenshots?.length) {
|
|
133
|
+
lines.push(' <screenshots>');
|
|
134
|
+
f.screenshots.forEach((s, i) => {
|
|
135
|
+
const type = s.type ?? (i === 0 ? 'default' : undefined);
|
|
136
|
+
const typeAttr = type ? ` type="${escapeXml(type)}"` : '';
|
|
137
|
+
const envAttr = s.environment ? ` environment="${escapeXml(s.environment)}"` : '';
|
|
138
|
+
lines.push(` <screenshot${typeAttr}${envAttr}>`);
|
|
139
|
+
lines.push(` <image>${escapeXml(s.url)}</image>`);
|
|
140
|
+
if (s.caption) {
|
|
141
|
+
pushTranslatorHint(lines, s.captionTranslatorHint, ' ');
|
|
142
|
+
lines.push(` <caption>${escapeXml(s.caption)}</caption>`);
|
|
143
|
+
}
|
|
144
|
+
lines.push(' </screenshot>');
|
|
145
|
+
});
|
|
146
|
+
lines.push(' </screenshots>');
|
|
147
|
+
}
|
|
148
|
+
// <url> entries
|
|
149
|
+
if (f.homepageUrl)
|
|
150
|
+
lines.push(` <url type="homepage">${escapeXml(f.homepageUrl)}</url>`);
|
|
151
|
+
if (f.bugtrackerUrl)
|
|
152
|
+
lines.push(` <url type="bugtracker">${escapeXml(f.bugtrackerUrl)}</url>`);
|
|
153
|
+
if (f.vcsBrowserUrl)
|
|
154
|
+
lines.push(` <url type="vcs-browser">${escapeXml(f.vcsBrowserUrl)}</url>`);
|
|
155
|
+
if (f.donationUrl)
|
|
156
|
+
lines.push(` <url type="donation">${escapeXml(f.donationUrl)}</url>`);
|
|
157
|
+
if (f.translateUrl)
|
|
158
|
+
lines.push(` <url type="translate">${escapeXml(f.translateUrl)}</url>`);
|
|
159
|
+
// <content_rating>
|
|
160
|
+
const cr = normaliseContentRating(f.contentRating);
|
|
161
|
+
if (cr.attributes && Object.keys(cr.attributes).length > 0) {
|
|
162
|
+
lines.push(` <content_rating type="${escapeXml(cr.type)}">`);
|
|
163
|
+
for (const [key, value] of Object.entries(cr.attributes)) {
|
|
164
|
+
lines.push(` <content_attribute id="${escapeXml(key)}">${escapeXml(value)}</content_attribute>`);
|
|
165
|
+
}
|
|
166
|
+
lines.push(' </content_rating>');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
lines.push(` <content_rating type="${escapeXml(cr.type)}" />`);
|
|
170
|
+
}
|
|
171
|
+
// <releases>
|
|
172
|
+
if (f.releases?.length) {
|
|
173
|
+
lines.push(' <releases>');
|
|
174
|
+
for (const r of f.releases) {
|
|
175
|
+
if (r.description === undefined) {
|
|
176
|
+
lines.push(` <release version="${escapeXml(r.version)}" date="${escapeXml(r.date)}" />`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
lines.push(` <release version="${escapeXml(r.version)}" date="${escapeXml(r.date)}">`);
|
|
180
|
+
lines.push(' <description>');
|
|
181
|
+
for (const blockLine of renderDescriptionBlocks(r.description, ' ')) {
|
|
182
|
+
lines.push(blockLine);
|
|
183
|
+
}
|
|
184
|
+
lines.push(' </description>');
|
|
185
|
+
lines.push(' </release>');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
lines.push(' </releases>');
|
|
189
|
+
}
|
|
190
|
+
// <categories>
|
|
191
|
+
if (f.categories?.length) {
|
|
192
|
+
lines.push(' <categories>');
|
|
193
|
+
for (const c of f.categories)
|
|
194
|
+
lines.push(` <category>${escapeXml(c)}</category>`);
|
|
195
|
+
lines.push(' </categories>');
|
|
196
|
+
}
|
|
197
|
+
// <keywords>
|
|
198
|
+
if (f.keywords?.length) {
|
|
199
|
+
lines.push(' <keywords>');
|
|
200
|
+
for (const k of f.keywords)
|
|
201
|
+
lines.push(` <keyword>${escapeXml(k)}</keyword>`);
|
|
202
|
+
lines.push(' </keywords>');
|
|
203
|
+
}
|
|
204
|
+
// <branding> (apps only — Flathub ignores it on CLI)
|
|
205
|
+
if (kind === 'desktop-application' && f.branding) {
|
|
206
|
+
lines.push(' <branding>');
|
|
207
|
+
lines.push(` <color type="primary" scheme_preference="light">${escapeXml(f.branding.accentLight)}</color>`);
|
|
208
|
+
lines.push(` <color type="primary" scheme_preference="dark">${escapeXml(f.branding.accentDark)}</color>`);
|
|
209
|
+
lines.push(' </branding>');
|
|
210
|
+
}
|
|
211
|
+
// <kudos>
|
|
212
|
+
if (f.kudos?.length) {
|
|
213
|
+
lines.push(' <kudos>');
|
|
214
|
+
for (const k of f.kudos)
|
|
215
|
+
lines.push(` <kudo>${escapeXml(k)}</kudo>`);
|
|
216
|
+
lines.push(' </kudos>');
|
|
217
|
+
}
|
|
218
|
+
// <provides> — always emit <binary> for both kinds; <mediatype>/<dbus> only when configured
|
|
219
|
+
const binaries = f.provides?.binaries ?? [inputs.command];
|
|
220
|
+
const mimetypes = f.provides?.mimetypes ?? [];
|
|
221
|
+
const dbus = f.provides?.dbus ?? [];
|
|
222
|
+
if (binaries.length || mimetypes.length || dbus.length) {
|
|
223
|
+
lines.push(' <provides>');
|
|
224
|
+
for (const b of binaries)
|
|
225
|
+
lines.push(` <binary>${escapeXml(b)}</binary>`);
|
|
226
|
+
for (const m of mimetypes)
|
|
227
|
+
lines.push(` <mediatype>${escapeXml(m)}</mediatype>`);
|
|
228
|
+
for (const d of dbus)
|
|
229
|
+
lines.push(` <dbus type="${escapeXml(d.type)}">${escapeXml(d.id)}</dbus>`);
|
|
230
|
+
lines.push(' </provides>');
|
|
231
|
+
}
|
|
232
|
+
// <supports>
|
|
233
|
+
if (f.supports?.controls?.length || f.supports?.internet) {
|
|
234
|
+
lines.push(' <supports>');
|
|
235
|
+
for (const c of f.supports.controls ?? [])
|
|
236
|
+
lines.push(` <control>${escapeXml(c)}</control>`);
|
|
237
|
+
if (f.supports.internet)
|
|
238
|
+
lines.push(` <internet>${escapeXml(f.supports.internet)}</internet>`);
|
|
239
|
+
lines.push(' </supports>');
|
|
240
|
+
}
|
|
241
|
+
// <requires>
|
|
242
|
+
if (f.requires?.displayLengthMin || f.requires?.controls?.length || f.requires?.internet) {
|
|
243
|
+
lines.push(' <requires>');
|
|
244
|
+
if (f.requires.displayLengthMin) {
|
|
245
|
+
lines.push(` <display_length compare="ge">${f.requires.displayLengthMin}</display_length>`);
|
|
246
|
+
}
|
|
247
|
+
for (const c of f.requires.controls ?? [])
|
|
248
|
+
lines.push(` <control>${escapeXml(c)}</control>`);
|
|
249
|
+
if (f.requires.internet)
|
|
250
|
+
lines.push(` <internet>${escapeXml(f.requires.internet)}</internet>`);
|
|
251
|
+
lines.push(' </requires>');
|
|
252
|
+
}
|
|
253
|
+
// <recommends>
|
|
254
|
+
if (f.recommends?.displayLengthMin || f.recommends?.controls?.length) {
|
|
255
|
+
lines.push(' <recommends>');
|
|
256
|
+
if (f.recommends.displayLengthMin) {
|
|
257
|
+
lines.push(` <display_length compare="ge">${f.recommends.displayLengthMin}</display_length>`);
|
|
258
|
+
}
|
|
259
|
+
for (const c of f.recommends.controls ?? [])
|
|
260
|
+
lines.push(` <control>${escapeXml(c)}</control>`);
|
|
261
|
+
lines.push(' </recommends>');
|
|
262
|
+
}
|
|
263
|
+
lines.push('</component>');
|
|
264
|
+
return lines.join('\n') + '\n';
|
|
265
|
+
}
|
|
266
|
+
// ─── Description block renderer ──────────────────────────────────────────
|
|
267
|
+
function renderDescriptionBlocks(description, indent) {
|
|
268
|
+
const blocks = typeof description === 'string'
|
|
269
|
+
? stringToBlocks(description)
|
|
270
|
+
: description;
|
|
271
|
+
const out = [];
|
|
272
|
+
for (const block of blocks) {
|
|
273
|
+
if ('p' in block) {
|
|
274
|
+
pushTranslatorHint(out, block.translatorHint, indent);
|
|
275
|
+
out.push(`${indent}<p>${escapeXml(block.p.trim().replace(/\s+/g, ' '))}</p>`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
pushTranslatorHint(out, block.translatorHint, indent);
|
|
279
|
+
out.push(`${indent}<ul>`);
|
|
280
|
+
for (const item of block.ul) {
|
|
281
|
+
if (typeof item === 'string') {
|
|
282
|
+
out.push(`${indent} <li>${escapeXml(item)}</li>`);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
pushTranslatorHint(out, item.translatorHint, `${indent} `);
|
|
286
|
+
out.push(`${indent} <li>${escapeXml(item.item)}</li>`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
out.push(`${indent}</ul>`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return out;
|
|
293
|
+
}
|
|
294
|
+
/** Auto-convert blank-line-split string into paragraph blocks. */
|
|
295
|
+
function stringToBlocks(s) {
|
|
296
|
+
return s
|
|
297
|
+
.trim()
|
|
298
|
+
.split(/\n\n+/)
|
|
299
|
+
.map((para) => ({ p: para.trim().replace(/\s+/g, ' ') }));
|
|
300
|
+
}
|
|
301
|
+
function pushTranslatorHint(out, hint, indent) {
|
|
302
|
+
if (!hint)
|
|
303
|
+
return;
|
|
304
|
+
out.push(`${indent}<!-- TRANSLATORS: ${hint} -->`);
|
|
305
|
+
}
|
|
306
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
307
|
+
function normaliseContentRating(cr) {
|
|
308
|
+
if (cr === undefined)
|
|
309
|
+
return { type: 'oars-1.1' };
|
|
310
|
+
if (typeof cr === 'string')
|
|
311
|
+
return { type: cr };
|
|
312
|
+
return { type: cr.type ?? 'oars-1.1', attributes: cr.attributes };
|
|
313
|
+
}
|
|
314
|
+
function substitute(template, tokens) {
|
|
315
|
+
let out = template;
|
|
316
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
317
|
+
out = out.split(`{{${key}}}`).join(value);
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
function escapeXml(value) {
|
|
322
|
+
return value
|
|
323
|
+
.replace(/&/g, '&')
|
|
324
|
+
.replace(/</g, '<')
|
|
325
|
+
.replace(/>/g, '>')
|
|
326
|
+
.replace(/"/g, '"');
|
|
327
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -233,4 +233,205 @@ export interface ConfigDataFlatpak {
|
|
|
233
233
|
ciContainer?: string;
|
|
234
234
|
/** Branches the generated workflow triggers on. Default `['main']`. */
|
|
235
235
|
ciBranches?: string[];
|
|
236
|
+
/**
|
|
237
|
+
* App kind. `'app'` (default) → desktop-application MetaInfo, GUI
|
|
238
|
+
* finish-args, .desktop + icon required. `'cli'` → console-application
|
|
239
|
+
* MetaInfo with `<provides><binary>`, no .desktop, flathub.json sets
|
|
240
|
+
* `skip-icons-check`. Supersedes the older `--cli-only` flag on
|
|
241
|
+
* `gjsify flatpak init`.
|
|
242
|
+
*/
|
|
243
|
+
kind?: 'app' | 'cli';
|
|
244
|
+
/**
|
|
245
|
+
* App display name (`.desktop` `Name=` + MetaInfo `<name>`). Defaults
|
|
246
|
+
* to a friendly derivation of `package.json#name` — that works when
|
|
247
|
+
* `name` is the reverse-DNS app id, but breaks when it's an npm
|
|
248
|
+
* package name like `learn6502`. Set this explicitly to the
|
|
249
|
+
* human-readable name shown in app stores (e.g. `"Learn 6502 Assembly"`).
|
|
250
|
+
*/
|
|
251
|
+
name?: string;
|
|
252
|
+
/**
|
|
253
|
+
* Developer attribution required by Flathub. `id` must be reverse-DNS.
|
|
254
|
+
* `email` (optional) becomes `<email>` inside `<developer>`.
|
|
255
|
+
* `nameTranslatable: false` (default) emits `translate="no"` on the
|
|
256
|
+
* `<name>` tag — recommended for personal/brand names that should not
|
|
257
|
+
* be translated. Set to `true` if the name is a descriptive phrase
|
|
258
|
+
* that translators should localise.
|
|
259
|
+
*/
|
|
260
|
+
developer?: {
|
|
261
|
+
id: string;
|
|
262
|
+
name: string;
|
|
263
|
+
email?: string;
|
|
264
|
+
nameTranslatable?: boolean;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* One-line summary, ≤80 chars, no trailing period (Flathub rule).
|
|
268
|
+
* Translatable — gettext's `msgfmt --xml --template` substitutes
|
|
269
|
+
* `<summary>` from `.po` files at build time. Set
|
|
270
|
+
* `summaryTranslatorHint` to emit a `<!-- TRANSLATORS: ... -->`
|
|
271
|
+
* comment before the tag.
|
|
272
|
+
*/
|
|
273
|
+
summary?: string;
|
|
274
|
+
/** Translator hint emitted as `<!-- TRANSLATORS: ... -->` before `<summary>`. */
|
|
275
|
+
summaryTranslatorHint?: string;
|
|
276
|
+
/**
|
|
277
|
+
* Long description. Two forms:
|
|
278
|
+
* - **String** — split on blank lines into `<p>` blocks. Best for
|
|
279
|
+
* simple descriptions without bullet lists.
|
|
280
|
+
* - **Block array** — explicit blocks (`{p:..., translatorHint?:...}`
|
|
281
|
+
* for paragraphs or `{ul:[...], translatorHint?:...}` for bullet
|
|
282
|
+
* lists). Each block can carry its own translator hint. Use this
|
|
283
|
+
* form when you need bullet lists or per-string translator context.
|
|
284
|
+
*/
|
|
285
|
+
description?: string | DescriptionBlock[];
|
|
286
|
+
/** Project homepage URL. Recommended; required for Flathub submission. */
|
|
287
|
+
homepageUrl?: string;
|
|
288
|
+
/** Bug tracker URL. */
|
|
289
|
+
bugtrackerUrl?: string;
|
|
290
|
+
/** VCS browser URL (e.g. GitHub repo). */
|
|
291
|
+
vcsBrowserUrl?: string;
|
|
292
|
+
/** Donation URL (e.g. OpenCollective / GitHub Sponsors). */
|
|
293
|
+
donationUrl?: string;
|
|
294
|
+
/**
|
|
295
|
+
* License SPDX identifiers. `project` is the project's source license
|
|
296
|
+
* (mandatory). `metadata` is the license under which the MetaInfo XML
|
|
297
|
+
* is distributed (default `'CC0-1.0'`).
|
|
298
|
+
*/
|
|
299
|
+
license?: {
|
|
300
|
+
metadata?: string;
|
|
301
|
+
project: string;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Content-rating policy. Two forms:
|
|
305
|
+
* - **String** — just the spec keyword (default `'oars-1.1'`), emits
|
|
306
|
+
* an empty `<content_rating type="..."/>` block.
|
|
307
|
+
* - **Object** — keyword + `attributes` map. Each attribute is an
|
|
308
|
+
* OARS key (`violence-cartoon`, `social-info`, etc.) → severity
|
|
309
|
+
* (`none`, `mild`, `moderate`, `intense`). Flathub recommends
|
|
310
|
+
* declaring attributes explicitly even when they're `none` so the
|
|
311
|
+
* rating audit is auditable.
|
|
312
|
+
*/
|
|
313
|
+
contentRating?: string | {
|
|
314
|
+
type?: string;
|
|
315
|
+
attributes?: Record<string, 'none' | 'mild' | 'moderate' | 'intense'>;
|
|
316
|
+
};
|
|
317
|
+
/** Freedesktop Menu categories (e.g. `['Development', 'Utility']`). */
|
|
318
|
+
categories?: string[];
|
|
319
|
+
/** Search keywords for app stores. */
|
|
320
|
+
keywords?: string[];
|
|
321
|
+
/**
|
|
322
|
+
* Release history. Most recent first. Each entry produces a
|
|
323
|
+
* `<release version=… date=…>` block. `description` accepts the
|
|
324
|
+
* same string-or-block-array shape as the top-level `description`
|
|
325
|
+
* field — use the array form for release notes with bullet lists
|
|
326
|
+
* or per-string translator hints.
|
|
327
|
+
*/
|
|
328
|
+
releases?: Array<{
|
|
329
|
+
version: string;
|
|
330
|
+
date: string;
|
|
331
|
+
description?: string | DescriptionBlock[];
|
|
332
|
+
}>;
|
|
333
|
+
/**
|
|
334
|
+
* Screenshots for app-stores. `url` is an absolute HTTPS URL to a PNG.
|
|
335
|
+
* `caption` is optional and translatable — set `captionTranslatorHint`
|
|
336
|
+
* for a `<!-- TRANSLATORS: ... -->` hint. `environment` is one of
|
|
337
|
+
* `'plasma'|'gnome'|'cli'` — Flathub uses it to group by desktop.
|
|
338
|
+
* First entry defaults to `type="default"`; override with `type`.
|
|
339
|
+
*/
|
|
340
|
+
screenshots?: Array<{
|
|
341
|
+
url: string;
|
|
342
|
+
caption?: string;
|
|
343
|
+
captionTranslatorHint?: string;
|
|
344
|
+
environment?: 'plasma' | 'gnome' | 'cli';
|
|
345
|
+
type?: 'default' | 'source';
|
|
346
|
+
}>;
|
|
347
|
+
/** Light/dark accent colours (hex `#rrggbb`) — emit `<branding>` block. */
|
|
348
|
+
branding?: {
|
|
349
|
+
accentLight: string;
|
|
350
|
+
accentDark: string;
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* Path to a scalable SVG icon. Flathub requires SVG (`/app/share/icons/
|
|
354
|
+
* hicolor/scalable/apps/<app-id>.svg`). When set, init verifies the file
|
|
355
|
+
* exists; when unset on `--kind app`, init prints a Flathub hint.
|
|
356
|
+
*/
|
|
357
|
+
icon?: string;
|
|
358
|
+
/**
|
|
359
|
+
* Remote-hosted icon URL — emitted as `<icon type="remote">`. Useful
|
|
360
|
+
* for the Flathub app-store thumbnail before the local SVG ships.
|
|
361
|
+
*/
|
|
362
|
+
iconRemote?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Translation platform URL (Weblate, Crowdin, Transifex, etc.).
|
|
365
|
+
* Emitted as `<url type="translate">`. Set this when your app accepts
|
|
366
|
+
* community translation contributions through a hosted platform.
|
|
367
|
+
*/
|
|
368
|
+
translateUrl?: string;
|
|
369
|
+
/**
|
|
370
|
+
* AppStream kudos — Flathub recognises a fixed set of "well-behaved"
|
|
371
|
+
* markers. Common values: `ModernToolkit`, `HiDpiIcon`,
|
|
372
|
+
* `TouchscreenSupport`, `UserDocs`, `HighContrast`, `Notifications`,
|
|
373
|
+
* `SearchProvider`. Full list at
|
|
374
|
+
* https://www.freedesktop.org/software/appstream/docs/sect-Metadata-DesktopApps.html#tag-dapp-kudos
|
|
375
|
+
*/
|
|
376
|
+
kudos?: string[];
|
|
377
|
+
/**
|
|
378
|
+
* Things this app provides to the system. `<binary>` is auto-included
|
|
379
|
+
* with the value of `command` when omitted (apps + CLIs both need
|
|
380
|
+
* this for AppStream to register the binary correctly).
|
|
381
|
+
*/
|
|
382
|
+
provides?: {
|
|
383
|
+
binaries?: string[];
|
|
384
|
+
mimetypes?: string[];
|
|
385
|
+
dbus?: Array<{
|
|
386
|
+
type: 'user' | 'system';
|
|
387
|
+
id: string;
|
|
388
|
+
}>;
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Hardware controls the app supports (best-effort declaration —
|
|
392
|
+
* AppStream `<supports>`). Common values:
|
|
393
|
+
* `keyboard`, `pointing`, `touch`, `gamepad`, `tablet`, `console`,
|
|
394
|
+
* `vision`.
|
|
395
|
+
*/
|
|
396
|
+
supports?: {
|
|
397
|
+
controls?: Array<'keyboard' | 'pointing' | 'touch' | 'gamepad' | 'tablet' | 'console' | 'vision'>;
|
|
398
|
+
/** Internet connectivity requirement. */
|
|
399
|
+
internet?: 'always' | 'offline-only' | 'first-run';
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* Hard requirements — AppStream `<requires>`. App won't function
|
|
403
|
+
* without these. `displayLengthMin` is the minimum display length in
|
|
404
|
+
* pixels (logical units) — typical phone-portrait minimum is 360.
|
|
405
|
+
*/
|
|
406
|
+
requires?: {
|
|
407
|
+
displayLengthMin?: number;
|
|
408
|
+
internet?: 'always' | 'offline-only' | 'first-run';
|
|
409
|
+
controls?: Array<'keyboard' | 'pointing' | 'touch' | 'gamepad' | 'tablet' | 'console'>;
|
|
410
|
+
};
|
|
411
|
+
/**
|
|
412
|
+
* Soft recommendations — AppStream `<recommends>`. App works better
|
|
413
|
+
* with these but functions without them. `displayLengthMin` typical
|
|
414
|
+
* tablet-min recommendation is 480.
|
|
415
|
+
*/
|
|
416
|
+
recommends?: {
|
|
417
|
+
displayLengthMin?: number;
|
|
418
|
+
controls?: Array<'keyboard' | 'pointing' | 'touch' | 'gamepad' | 'tablet' | 'console'>;
|
|
419
|
+
};
|
|
236
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* A single block inside a MetaInfo `<description>`. Either a paragraph
|
|
423
|
+
* (`{p}`) or a bullet list (`{ul}`). Each block can carry an optional
|
|
424
|
+
* `translatorHint` that becomes a `<!-- TRANSLATORS: ... -->` comment
|
|
425
|
+
* before the block in the emitted `.metainfo.xml.in` template — gives
|
|
426
|
+
* translators context when the string lands in their `.po` file.
|
|
427
|
+
*/
|
|
428
|
+
export type DescriptionBlock = {
|
|
429
|
+
p: string;
|
|
430
|
+
translatorHint?: string;
|
|
431
|
+
} | {
|
|
432
|
+
ul: Array<string | {
|
|
433
|
+
item: string;
|
|
434
|
+
translatorHint?: string;
|
|
435
|
+
}>;
|
|
436
|
+
translatorHint?: string;
|
|
437
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"clear": "rm -rf lib dist tsconfig.tsbuildinfo || exit 0",
|
|
24
24
|
"check": "tsc --noEmit",
|
|
25
25
|
"start": "node lib/index.js",
|
|
26
|
-
"build": "tsc && mkdir -p lib/templates && cp -L src/templates/install.mjs.tmpl lib/templates/install.mjs.tmpl && gjsify run chmod",
|
|
26
|
+
"build": "tsc && mkdir -p lib/templates/flatpak && cp -L src/templates/install.mjs.tmpl lib/templates/install.mjs.tmpl && cp src/templates/flatpak/*.tmpl lib/templates/flatpak/ && gjsify run chmod",
|
|
27
27
|
"build:gjs-bundle": "node lib/index.js build src/index.ts --app gjs --outfile dist/cli.gjs.mjs --shebang",
|
|
28
28
|
"chmod": "chmod +x ./lib/index.js",
|
|
29
29
|
"build:test:node": "node lib/index.js build src/test.mts --app node --outfile dist/test.node.mjs",
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
"cli"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@gjsify/buffer": "^0.4.
|
|
41
|
-
"@gjsify/create-app": "^0.4.
|
|
42
|
-
"@gjsify/node-globals": "^0.4.
|
|
43
|
-
"@gjsify/node-polyfills": "^0.4.
|
|
44
|
-
"@gjsify/npm-registry": "^0.4.
|
|
45
|
-
"@gjsify/resolve-npm": "^0.4.
|
|
46
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.4.
|
|
47
|
-
"@gjsify/rolldown-plugin-pnp": "^0.4.
|
|
48
|
-
"@gjsify/semver": "^0.4.
|
|
49
|
-
"@gjsify/tar": "^0.4.
|
|
50
|
-
"@gjsify/web-polyfills": "^0.4.
|
|
51
|
-
"@gjsify/workspace": "^0.4.
|
|
40
|
+
"@gjsify/buffer": "^0.4.13",
|
|
41
|
+
"@gjsify/create-app": "^0.4.13",
|
|
42
|
+
"@gjsify/node-globals": "^0.4.13",
|
|
43
|
+
"@gjsify/node-polyfills": "^0.4.13",
|
|
44
|
+
"@gjsify/npm-registry": "^0.4.13",
|
|
45
|
+
"@gjsify/resolve-npm": "^0.4.13",
|
|
46
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.4.13",
|
|
47
|
+
"@gjsify/rolldown-plugin-pnp": "^0.4.13",
|
|
48
|
+
"@gjsify/semver": "^0.4.13",
|
|
49
|
+
"@gjsify/tar": "^0.4.13",
|
|
50
|
+
"@gjsify/web-polyfills": "^0.4.13",
|
|
51
|
+
"@gjsify/workspace": "^0.4.13",
|
|
52
52
|
"cosmiconfig": "^9.0.1",
|
|
53
53
|
"get-tsconfig": "^4.14.0",
|
|
54
54
|
"pkg-types": "^2.3.1",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"yargs": "^18.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@gjsify/unit": "^0.4.
|
|
59
|
+
"@gjsify/unit": "^0.4.13",
|
|
60
60
|
"@types/yargs": "^17.0.35",
|
|
61
61
|
"typescript": "^6.0.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@gjsify/rolldown-native": "^0.4.
|
|
64
|
+
"@gjsify/rolldown-native": "^0.4.13"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"@gjsify/rolldown-native": {
|