@gjsify/cli 0.16.9 → 0.18.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.
package/lib/cli-app.js CHANGED
@@ -21,7 +21,7 @@ import { fileURLToPath } from 'node:url';
21
21
  import yargs from 'yargs';
22
22
  import { buildCommand as build, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, 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, whoamiCommand as whoami, loginCommand as login, logoutCommand as logout, trustCommand as trust, onboardCommand as onboard, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, tscCommand as tsc, affectedCommand as affected, storybookCommand as storybook, debugCommand as debug, browseCommand as browse, } from './commands/index.js';
23
23
  import { APP_NAME } from './constants.js';
24
- import { isNode } from '@gjsify/rolldown-plugin-gjsify/runtime';
24
+ import { isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
25
25
  import { ensureGjsifyShimOnPath } from './utils/gjsify-shim.js';
26
26
  // Detect which runtime is executing the CLI (GJS or Node.js).
27
27
  // GJS MUST be checked first because @gjsify/process sets
@@ -29,9 +29,9 @@ import { ensureGjsifyShimOnPath } from './utils/gjsify-shim.js';
29
29
  // `process.versions.node` check would be a false Node positive under GJS.
30
30
  function runtimeLabel() {
31
31
  try {
32
- const sys = globalThis.imports?.system;
33
- if (sys?.version !== undefined) {
34
- const v = Number(sys.version);
32
+ const sysVersion = gjsSystemVersion();
33
+ if (sysVersion !== undefined) {
34
+ const v = Number(sysVersion);
35
35
  return `GJS ${Math.floor(v / 10000)}.${Math.floor((v % 10000) / 100)}.${v % 100} (SpiderMonkey)`;
36
36
  }
37
37
  }
@@ -16,22 +16,11 @@
16
16
  // (since the typescript-check binding is positional `[paths..]`).
17
17
  import { spawn, spawnSync } from 'node:child_process';
18
18
  import { prefixLines } from '../utils/prefixed-output.js';
19
- import { existsSync, readFileSync } from 'node:fs';
19
+ import { readPackageJson } from '../utils/pkg-json.js';
20
20
  import { join } from 'node:path';
21
21
  import { cpus } from 'node:os';
22
22
  import { discoverWorkspaces, filterWorkspaces } from '@gjsify/workspace';
23
23
  import { findWorkspaceRoot } from '../utils/workspace-root.js';
24
- function readPackageJson(dir) {
25
- const path = join(dir, 'package.json');
26
- if (!existsSync(path))
27
- return null;
28
- try {
29
- return JSON.parse(readFileSync(path, 'utf-8'));
30
- }
31
- catch {
32
- return null;
33
- }
34
- }
35
24
  /**
36
25
  * Run a single workspace's `check` script. Returns the exit code; non-zero
37
26
  * indicates failure. Output is forwarded to the parent's stdout/stderr,
@@ -108,7 +97,7 @@ export const checkCommand = {
108
97
  // through gjsify so the command surface stays uniform with format/
109
98
  // lint/fix.
110
99
  if (workspaceRoot && cwd !== workspaceRoot) {
111
- const pkg = readPackageJson(cwd);
100
+ const pkg = readPackageJson(join(cwd, 'package.json'));
112
101
  if (pkg?.scripts?.check) {
113
102
  if (args.verbose) {
114
103
  console.log(`[check] cwd=${cwd} → npm run check`);
@@ -31,6 +31,7 @@ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, rmSync, syml
31
31
  import { dirname, join, relative, resolve } from 'node:path';
32
32
  import { fileURLToPath } from 'node:url';
33
33
  import { spawn, spawnSync } from 'node:child_process';
34
+ import { gjsExit } from '@gjsify/rolldown-plugin-gjsify/runtime';
34
35
  import { discoverWorkspaces } from '@gjsify/workspace';
35
36
  import { buildInstallCommand, detectPackageManager, runMinimalChecks } from '../utils/check-system-deps.js';
36
37
  import { detectNativePackages } from '../utils/detect-native-packages.js';
@@ -247,11 +248,8 @@ function isAbortedFromOverallTimeout(err) {
247
248
  */
248
249
  function forceExit(code) {
249
250
  process.exitCode = code;
250
- const gjs = globalThis.imports;
251
- if (gjs?.system?.exit) {
252
- gjs.system.exit(code);
251
+ if (gjsExit(code))
253
252
  return;
254
- }
255
253
  process.exit(code);
256
254
  }
257
255
  const CLI_PACKAGE_NAME = '@gjsify/cli';
package/lib/index.js CHANGED
@@ -6,6 +6,7 @@
6
6
  // This file is just the executable wrapper around `runCli`.
7
7
  import { hideBin } from 'yargs/helpers';
8
8
  import { runCli } from './cli-app.js';
9
+ import { gjsExit } from '@gjsify/rolldown-plugin-gjsify/runtime';
9
10
  try {
10
11
  await runCli(hideBin(process.argv));
11
12
  }
@@ -23,6 +24,5 @@ catch (err) {
23
24
  // atexit hook and would exit 0 at natural shutdown, so force a non-zero exit
24
25
  // via `imports.system.exit` there (no-op on Node, where `imports` is unset).
25
26
  process.exitCode = 1;
26
- const gjs = globalThis.imports;
27
- gjs?.system?.exit?.(1);
27
+ gjsExit(1);
28
28
  }
@@ -10,8 +10,9 @@
10
10
  // their full transitive prebuild set picked up automatically. The
11
11
  // transitive walk is what makes `gjsify showcase` / `gjsify dlx` work
12
12
  // for packages whose Vala typelibs live in *indirect* deps.
13
- import { readdirSync, existsSync, readFileSync } from 'node:fs';
13
+ import { readdirSync, existsSync } from 'node:fs';
14
14
  import { join, resolve } from 'node:path';
15
+ import { readPackageJson } from './pkg-json.js';
15
16
  /** Map Node.js process.arch values to the convention used in prebuilds/ directories. */
16
17
  function nodeArchToLinuxArch(arch) {
17
18
  const map = {
@@ -22,15 +23,6 @@ function nodeArchToLinuxArch(arch) {
22
23
  };
23
24
  return map[arch] ?? arch;
24
25
  }
25
- /** Read a package.json file and return its parsed content, or null on error. */
26
- function readPackageJson(pkgJsonPath) {
27
- try {
28
- return JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
29
- }
30
- catch {
31
- return null;
32
- }
33
- }
34
26
  /**
35
27
  * Scan all packages in a node_modules directory for gjsify native prebuilds.
36
28
  * Handles scoped packages (@scope/name) as well as flat packages.
@@ -7,7 +7,7 @@
7
7
  // confusing crash with no actionable message (observed: Node 22 in a Fedora
8
8
  // container segfaults `gjsify install --backend native` during download/extract).
9
9
  // This preflight turns that into a clear, actionable error.
10
- import { isNode } from '@gjsify/rolldown-plugin-gjsify/runtime';
10
+ import { isNode, gjsSystemVersion } from '@gjsify/rolldown-plugin-gjsify/runtime';
11
11
  /**
12
12
  * Minimum Node.js major the native install backend supports — its prebuilt native
13
13
  * modules are ABI-locked to this line (kept in sync with `.nvmrc`).
@@ -39,7 +39,7 @@ function onRealNode() {
39
39
  // GJS exposes `imports.system.version`; @gjsify/process fakes
40
40
  // `process.versions.node = '20.0.0'` for compat, so check GJS FIRST — a plain
41
41
  // `process.versions.node` read would be a false Node positive under GJS.
42
- const sysVersion = globalThis.imports?.system?.version;
42
+ const sysVersion = gjsSystemVersion();
43
43
  if (sysVersion !== undefined)
44
44
  return false;
45
45
  return isNode();
@@ -54,7 +54,7 @@ import { pathToFileURL } from 'node:url';
54
54
  import { findWorkspaceRoot } from './workspace-root.js';
55
55
  import { resolveNpmPackage } from './resolve-npm-package.js';
56
56
  import { nodeBinary } from './run-node.js';
57
- import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
57
+ import { isGjs, gjsExit } from '@gjsify/rolldown-plugin-gjsify/runtime';
58
58
  /**
59
59
  * Map Node.js `process.platform` + `arch` (+ musl on Linux) to the
60
60
  * napi-rs binding-package suffix oxc uses, e.g. `linux-x64-gnu`,
@@ -408,9 +408,7 @@ export function setOxcExitCode(code) {
408
408
  process.exitCode = code;
409
409
  if (code === 0)
410
410
  return;
411
- const gjs = globalThis.imports;
412
- if (gjs?.system?.exit)
413
- gjs.system.exit(code);
411
+ gjsExit(code);
414
412
  }
415
413
  /**
416
414
  * Has the given oxc tool's npm package (or its companion) been declared in the
@@ -11,6 +11,8 @@ export interface PackageJson {
11
11
  devDependencies?: Record<string, string>;
12
12
  peerDependencies?: Record<string, string>;
13
13
  optionalDependencies?: Record<string, string>;
14
+ scripts?: Record<string, string>;
15
+ private?: boolean;
14
16
  [key: string]: unknown;
15
17
  }
16
18
  export declare function readPackageJson(pkgPath: string): PackageJson | null;
@@ -0,0 +1,6 @@
1
+ import type { PackageJson } from './pkg-json-edit.js';
2
+ /**
3
+ * Read + parse a `package.json` at `pkgPath`. Permissive: any error (missing
4
+ * file, unreadable, invalid JSON) yields `null`.
5
+ */
6
+ export declare function readPackageJson(pkgPath: string): PackageJson | null;
@@ -0,0 +1,24 @@
1
+ // Single permissive reader for `package.json` files, shared by the CLI's
2
+ // workspace- and dependency-discovery paths (`gjsify run`/`workspace`/`foreach`/
3
+ // `check`, native-prebuild scanning). Returns `null` when the file is missing
4
+ // OR unparseable — discovery must degrade gracefully on a stray or corrupt
5
+ // manifest rather than crash the whole command.
6
+ //
7
+ // The install-save path keeps its own STRICTER reader in `pkg-json-edit.ts`
8
+ // (`readPackageJson` there): it returns `null` only when the file is absent but
9
+ // THROWS on invalid JSON, so a corrupt manifest is never silently overwritten
10
+ // during `gjsify install <pkg>`. That contract is intentional and load-bearing
11
+ // — do not fold it into this one.
12
+ import { readFileSync } from 'node:fs';
13
+ /**
14
+ * Read + parse a `package.json` at `pkgPath`. Permissive: any error (missing
15
+ * file, unreadable, invalid JSON) yields `null`.
16
+ */
17
+ export function readPackageJson(pkgPath) {
18
+ try {
19
+ return JSON.parse(readFileSync(pkgPath, 'utf-8'));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
@@ -53,6 +53,7 @@ export async function runGjsBundle(bundlePath, extraArgs = [], options = {}) {
53
53
  const gjsCommand = ['gjs', ...gjsArgs.map((a) => (a.includes(' ') ? `"${a}"` : a))].join(' ');
54
54
  console.error(`$ ${envPrefix ? `${envPrefix} ` : ''}${gjsCommand}`);
55
55
  const child = spawn('gjs', gjsArgs, { env, stdio: 'inherit' });
56
+ let failed = false;
56
57
  await new Promise((resolvePromise, reject) => {
57
58
  child.on('close', (code) => {
58
59
  if (code !== 0) {
@@ -65,10 +66,22 @@ export async function runGjsBundle(bundlePath, extraArgs = [], options = {}) {
65
66
  child.on('error', reject);
66
67
  }).catch((err) => {
67
68
  console.error(err.message);
68
- process.exit(1);
69
+ failed = true;
69
70
  });
71
+ if (failed) {
72
+ // Set `process.exitCode` synchronously BEFORE exiting: under GJS
73
+ // `process.exit()` is deferred (the GLib main loop is armed), so when
74
+ // this runs via run.ts's in-process script dispatch, execution returns
75
+ // to the caller, which propagates by reading `process.exitCode`. Without
76
+ // this, `gjsify run <script>` whose body is `gjsify run <bundle>`
77
+ // swallowed a non-zero gjs exit and returned 0 — masking a failing
78
+ // `test:gjs` in the `test` script chain (and, via `gjsify foreach test`,
79
+ // in CI). The `return` guards against a fall-through second `exit`.
80
+ process.exitCode = 1;
81
+ return process.exit(1);
82
+ }
70
83
  // See RunGjsBundleOptions.exitOnSuccess — terminal callers opt in,
71
84
  // mid-flow callers (test.ts's runtime loop) keep the process alive.
72
85
  if (options.exitOnSuccess)
73
- process.exit(0);
86
+ return process.exit(0);
74
87
  }
@@ -11,17 +11,10 @@
11
11
  // monorepo must actually contain `start` as one of its workspaces.
12
12
  // Without this guard, a grand-parent monorepo unrelated to `start`
13
13
  // could be picked up.
14
- import { existsSync, readFileSync } from 'node:fs';
14
+ import { existsSync } from 'node:fs';
15
15
  import { join, resolve } from 'node:path';
16
16
  import { discoverWorkspaces } from '@gjsify/workspace';
17
- function readPackageJson(path) {
18
- try {
19
- return JSON.parse(readFileSync(path, 'utf-8'));
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
17
+ import { readPackageJson } from './pkg-json.js';
25
18
  export function findWorkspaceRoot(start) {
26
19
  let dir = start;
27
20
  for (let i = 0; i < 12; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.16.9",
3
+ "version": "0.18.0",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -122,19 +122,19 @@
122
122
  "cli"
123
123
  ],
124
124
  "dependencies": {
125
- "@gjsify/buffer": "^0.16.9",
126
- "@gjsify/create-app": "^0.16.9",
127
- "@gjsify/node-globals": "^0.16.9",
128
- "@gjsify/node-polyfills": "^0.16.9",
129
- "@gjsify/npm-registry": "^0.16.9",
130
- "@gjsify/resolve-npm": "^0.16.9",
131
- "@gjsify/rolldown-plugin-gjsify": "^0.16.9",
132
- "@gjsify/rolldown-plugin-pnp": "^0.16.9",
133
- "@gjsify/semver": "^0.16.9",
134
- "@gjsify/tar": "^0.16.9",
135
- "@gjsify/tsc": "^0.16.9",
136
- "@gjsify/web-polyfills": "^0.16.9",
137
- "@gjsify/workspace": "^0.16.9",
125
+ "@gjsify/buffer": "^0.18.0",
126
+ "@gjsify/create-app": "^0.18.0",
127
+ "@gjsify/node-globals": "^0.18.0",
128
+ "@gjsify/node-polyfills": "^0.18.0",
129
+ "@gjsify/npm-registry": "^0.18.0",
130
+ "@gjsify/resolve-npm": "^0.18.0",
131
+ "@gjsify/rolldown-plugin-gjsify": "^0.18.0",
132
+ "@gjsify/rolldown-plugin-pnp": "^0.18.0",
133
+ "@gjsify/semver": "^0.18.0",
134
+ "@gjsify/tar": "^0.18.0",
135
+ "@gjsify/tsc": "^0.18.0",
136
+ "@gjsify/web-polyfills": "^0.18.0",
137
+ "@gjsify/workspace": "^0.18.0",
138
138
  "cosmiconfig": "^9.0.2",
139
139
  "get-tsconfig": "^4.14.0",
140
140
  "pkg-types": "^2.3.1",
@@ -142,13 +142,13 @@
142
142
  "yargs": "^18.0.0"
143
143
  },
144
144
  "devDependencies": {
145
- "@gjsify/unit": "^0.16.9",
145
+ "@gjsify/unit": "^0.18.0",
146
146
  "@types/yargs": "^17.0.35",
147
147
  "typescript": "^6.0.3"
148
148
  },
149
149
  "peerDependencies": {
150
- "@gjsify/oxfmt-native": "^0.16.9",
151
- "@gjsify/rolldown-native": "^0.16.9"
150
+ "@gjsify/oxfmt-native": "^0.18.0",
151
+ "@gjsify/rolldown-native": "^0.18.0"
152
152
  },
153
153
  "peerDependenciesMeta": {
154
154
  "@gjsify/oxfmt-native": {