@gjsify/cli 0.10.0 → 0.12.0

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.
@@ -10,6 +10,47 @@ export interface GlobalLayout {
10
10
  * `GJSIFY_GLOBAL_PREFIX` and `GJSIFY_GLOBAL_BIN_DIR` escape hatches for tests.
11
11
  */
12
12
  export declare function defaultGlobalLayout(): GlobalLayout;
13
+ /**
14
+ * The GJS-native tooling bridges the global CLI needs to run a build under GJS.
15
+ *
16
+ * These are declared OPTIONAL peer dependencies of `@gjsify/cli` (so a plain
17
+ * `npm install @gjsify/cli` on Node — where the npm `rolldown` / `lightningcss`
18
+ * / `oxfmt` crates work — does not force a linux prebuild fetch). But a *global
19
+ * GJS install* exists precisely to run the GJS CLI, where there is NO npm
20
+ * fallback: `@gjsify/rolldown-native` is the only bundler engine (`gjsify build`
21
+ * hard-fails without it), `@gjsify/lightningcss-native` backs CSS lowering, and
22
+ * `@gjsify/oxfmt-native` backs `gjsify format`/`fix`. So the global installer
23
+ * lays them down explicitly, on top of the cli's resolved runtime deps.
24
+ *
25
+ * `@gjsify/rolldown-native` is the load-bearing one (build is dead without it);
26
+ * the other two degrade gracefully at runtime (CSS / format fall back), so all
27
+ * three are installed best-effort: a platform with no published prebuild for
28
+ * the engine just gets a warning + continue, mirroring how the optional-peer
29
+ * story already degrades on Node.
30
+ */
31
+ export declare const GJS_ENGINE_PACKAGES: readonly ["@gjsify/rolldown-native", "@gjsify/lightningcss-native", "@gjsify/oxfmt-native"];
32
+ /** True when `<prefix>/node_modules/@gjsify/rolldown-native` is laid down. */
33
+ export declare function hasBundlerEngineInstalled(prefix: string): boolean;
34
+ /**
35
+ * Best-effort install of the GJS-native engine packages into a global prefix.
36
+ *
37
+ * Each engine is installed in its own `installPackages` call so one engine that
38
+ * has no published prebuild for the current platform (the resolver throws "No
39
+ * version of … satisfies …", or the registry 404s) does NOT abort the others —
40
+ * it warns and continues. The bundler engine (`@gjsify/rolldown-native`) is the
41
+ * one a build truly needs; the failure surfaces a clear, actionable message.
42
+ *
43
+ * `version` is the dist-tag / version the top-level `@gjsify/cli` resolved to,
44
+ * so the engines move in lockstep with the bundle. It may be a concrete version
45
+ * (`0.11.0`) or a dist-tag (`latest`, `next`); both are valid spec suffixes.
46
+ *
47
+ * @param installFn Injected to keep this unit-testable without a real registry —
48
+ * defaults to the production `installPackages` backend.
49
+ */
50
+ export declare function installGjsEnginePackages(prefix: string, version: string, opts?: {
51
+ verbose?: boolean;
52
+ installFn?: (spec: string) => Promise<void>;
53
+ }): Promise<void>;
13
54
  export interface LinkedBin {
14
55
  /** The bin-name (key from `bin` / `gjsify.bin`). */
15
56
  name: string;
@@ -39,6 +39,80 @@ export function defaultGlobalLayout() {
39
39
  binDir: binOverride ?? path.join(home, '.local', 'bin'),
40
40
  };
41
41
  }
42
+ /**
43
+ * The GJS-native tooling bridges the global CLI needs to run a build under GJS.
44
+ *
45
+ * These are declared OPTIONAL peer dependencies of `@gjsify/cli` (so a plain
46
+ * `npm install @gjsify/cli` on Node — where the npm `rolldown` / `lightningcss`
47
+ * / `oxfmt` crates work — does not force a linux prebuild fetch). But a *global
48
+ * GJS install* exists precisely to run the GJS CLI, where there is NO npm
49
+ * fallback: `@gjsify/rolldown-native` is the only bundler engine (`gjsify build`
50
+ * hard-fails without it), `@gjsify/lightningcss-native` backs CSS lowering, and
51
+ * `@gjsify/oxfmt-native` backs `gjsify format`/`fix`. So the global installer
52
+ * lays them down explicitly, on top of the cli's resolved runtime deps.
53
+ *
54
+ * `@gjsify/rolldown-native` is the load-bearing one (build is dead without it);
55
+ * the other two degrade gracefully at runtime (CSS / format fall back), so all
56
+ * three are installed best-effort: a platform with no published prebuild for
57
+ * the engine just gets a warning + continue, mirroring how the optional-peer
58
+ * story already degrades on Node.
59
+ */
60
+ export const GJS_ENGINE_PACKAGES = [
61
+ '@gjsify/rolldown-native',
62
+ '@gjsify/lightningcss-native',
63
+ '@gjsify/oxfmt-native',
64
+ ];
65
+ /** True when `<prefix>/node_modules/@gjsify/rolldown-native` is laid down. */
66
+ export function hasBundlerEngineInstalled(prefix) {
67
+ return fs.existsSync(path.join(prefix, 'node_modules', '@gjsify/rolldown-native', 'package.json'));
68
+ }
69
+ /**
70
+ * Best-effort install of the GJS-native engine packages into a global prefix.
71
+ *
72
+ * Each engine is installed in its own `installPackages` call so one engine that
73
+ * has no published prebuild for the current platform (the resolver throws "No
74
+ * version of … satisfies …", or the registry 404s) does NOT abort the others —
75
+ * it warns and continues. The bundler engine (`@gjsify/rolldown-native`) is the
76
+ * one a build truly needs; the failure surfaces a clear, actionable message.
77
+ *
78
+ * `version` is the dist-tag / version the top-level `@gjsify/cli` resolved to,
79
+ * so the engines move in lockstep with the bundle. It may be a concrete version
80
+ * (`0.11.0`) or a dist-tag (`latest`, `next`); both are valid spec suffixes.
81
+ *
82
+ * @param installFn Injected to keep this unit-testable without a real registry —
83
+ * defaults to the production `installPackages` backend.
84
+ */
85
+ export async function installGjsEnginePackages(prefix, version, opts = {}) {
86
+ const verbose = opts.verbose ?? false;
87
+ const installOne = opts.installFn ??
88
+ (async (spec) => {
89
+ // Lazy import to avoid a static cycle (install-backend → … → here)
90
+ // and to keep this module importable from the lighter unit tests.
91
+ const { installPackages } = await import('./install-backend.js');
92
+ await installPackages({ prefix, specs: [spec], verbose });
93
+ });
94
+ for (const name of GJS_ENGINE_PACKAGES) {
95
+ const spec = version ? `${name}@${version}` : name;
96
+ try {
97
+ await installOne(spec);
98
+ }
99
+ catch (err) {
100
+ const msg = err instanceof Error ? err.message : String(err);
101
+ // `@gjsify/rolldown-native` is the bundler engine — its absence
102
+ // means `gjsify build` (and storybook / anything that builds) will
103
+ // hard-fail under GJS. Make that one loud; the other two degrade
104
+ // gracefully so a plain note suffices.
105
+ if (name === '@gjsify/rolldown-native') {
106
+ console.warn(`\nWarning: could not install the GJS bundler engine ${name}@${version} — ${msg}\n` +
107
+ ` \`gjsify build\` (and storybook / flatpak) will fail under GJS until it is present.\n` +
108
+ ` This usually means no prebuild is published for this platform (${process.platform}/${process.arch}).`);
109
+ }
110
+ else if (verbose) {
111
+ console.warn(`gjsify install: optional GJS engine ${name}@${version} not installed — ${msg}`);
112
+ }
113
+ }
114
+ }
115
+ }
42
116
  /**
43
117
  * Install each top-level package's bin entries into `binDir` as small POSIX
44
118
  * `sh` launchers that exec the real bin. Reads each package's installed
@@ -73,14 +73,38 @@ function makeTtyReporter(stream) {
73
73
  },
74
74
  };
75
75
  }
76
- // ──── Plain reporter — one line per phase begin + end (non-TTY) ────────────
76
+ // ──── Plain reporter — phase begin/end + periodic heartbeat (non-TTY) ──────
77
+ //
78
+ // On a non-TTY (piped to a log file, CI, or captured output) the bar can't
79
+ // `\r`-overwrite, so per-package lines are suppressed to avoid flooding the
80
+ // log. But emitting NOTHING between `beginPhase` and `endPhase` makes a long
81
+ // download/extract phase look frozen — the exact "no further output for 25+
82
+ // minutes" symptom of a slow install. So we emit a low-frequency heartbeat:
83
+ // at most one line per HEARTBEAT_MS, AND only after at least a few packages of
84
+ // real progress, so a fast warm install (most nodes skipped) stays quiet while
85
+ // a genuinely long phase reports `… N/M (X%)` every few seconds. This mirrors
86
+ // what npm (`--loglevel http`) and pnpm log on non-TTY: progress you can watch
87
+ // without it being one line per file.
88
+ const HEARTBEAT_MS = 5_000;
77
89
  function makePlainReporter(stream) {
90
+ let lastHeartbeat = 0;
91
+ let lastReported = 0;
78
92
  return {
79
93
  beginPhase(phase, total) {
94
+ lastHeartbeat = Date.now();
95
+ lastReported = 0;
80
96
  stream.write(`gjsify install: ${PHASE_LABEL[phase]} ${total} package(s)\n`);
81
97
  },
82
- update() {
83
- // Per-package output would flood logs; skip on non-TTY.
98
+ update(ev) {
99
+ const now = Date.now();
100
+ if (now - lastHeartbeat < HEARTBEAT_MS)
101
+ return;
102
+ if (ev.current <= lastReported)
103
+ return;
104
+ lastHeartbeat = now;
105
+ lastReported = ev.current;
106
+ const pct = ev.total > 0 ? Math.round((ev.current / ev.total) * 100) : 0;
107
+ stream.write(`gjsify install: ${PHASE_LABEL[ev.phase]} ${ev.current}/${ev.total} (${pct}%)\n`);
84
108
  },
85
109
  endPhase(phase) {
86
110
  stream.write(`gjsify install: ${PHASE_LABEL[phase]} done\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -120,19 +120,19 @@
120
120
  "cli"
121
121
  ],
122
122
  "dependencies": {
123
- "@gjsify/buffer": "^0.10.0",
124
- "@gjsify/create-app": "^0.10.0",
125
- "@gjsify/node-globals": "^0.10.0",
126
- "@gjsify/node-polyfills": "^0.10.0",
127
- "@gjsify/npm-registry": "^0.10.0",
128
- "@gjsify/resolve-npm": "^0.10.0",
129
- "@gjsify/rolldown-plugin-gjsify": "^0.10.0",
130
- "@gjsify/rolldown-plugin-pnp": "^0.10.0",
131
- "@gjsify/semver": "^0.10.0",
132
- "@gjsify/tar": "^0.10.0",
133
- "@gjsify/tsc": "^0.10.0",
134
- "@gjsify/web-polyfills": "^0.10.0",
135
- "@gjsify/workspace": "^0.10.0",
123
+ "@gjsify/buffer": "^0.12.0",
124
+ "@gjsify/create-app": "^0.12.0",
125
+ "@gjsify/node-globals": "^0.12.0",
126
+ "@gjsify/node-polyfills": "^0.12.0",
127
+ "@gjsify/npm-registry": "^0.12.0",
128
+ "@gjsify/resolve-npm": "^0.12.0",
129
+ "@gjsify/rolldown-plugin-gjsify": "^0.12.0",
130
+ "@gjsify/rolldown-plugin-pnp": "^0.12.0",
131
+ "@gjsify/semver": "^0.12.0",
132
+ "@gjsify/tar": "^0.12.0",
133
+ "@gjsify/tsc": "^0.12.0",
134
+ "@gjsify/web-polyfills": "^0.12.0",
135
+ "@gjsify/workspace": "^0.12.0",
136
136
  "cosmiconfig": "^9.0.2",
137
137
  "get-tsconfig": "^4.14.0",
138
138
  "pkg-types": "^2.3.1",
@@ -140,13 +140,13 @@
140
140
  "yargs": "^18.0.0"
141
141
  },
142
142
  "devDependencies": {
143
- "@gjsify/unit": "^0.10.0",
143
+ "@gjsify/unit": "^0.12.0",
144
144
  "@types/yargs": "^17.0.35",
145
145
  "typescript": "^6.0.3"
146
146
  },
147
147
  "peerDependencies": {
148
- "@gjsify/oxfmt-native": "^0.10.0",
149
- "@gjsify/rolldown-native": "^0.10.0"
148
+ "@gjsify/oxfmt-native": "^0.12.0",
149
+ "@gjsify/rolldown-native": "^0.12.0"
150
150
  },
151
151
  "peerDependenciesMeta": {
152
152
  "@gjsify/oxfmt-native": {
package/showcases.json CHANGED
@@ -49,6 +49,13 @@
49
49
  "package": "@gjsify/example-node-express-webserver",
50
50
  "description": "Express web server on GJS via @gjsify/http",
51
51
  "needsWebgl": false
52
+ },
53
+ {
54
+ "name": "adwaita-storybook",
55
+ "category": "gtk",
56
+ "package": "@gjsify/example-gtk-adwaita-storybook",
57
+ "description": "Interactive Libadwaita component browser — author one story per widget, rendered natively (GTK) and in the browser",
58
+ "needsWebgl": false
52
59
  }
53
60
  ]
54
61
  }