@gjsify/cli 0.3.16 → 0.3.18
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/commands/build.js +13 -4
- package/lib/commands/showcase.js +9 -6
- package/lib/config.js +12 -0
- package/lib/utils/detect-native-packages.d.ts +0 -13
- package/lib/utils/detect-native-packages.js +10 -74
- package/lib/utils/run-gjs.d.ts +15 -5
- package/lib/utils/run-gjs.js +42 -22
- package/package.json +11 -11
- package/src/commands/build.ts +13 -4
- package/src/commands/showcase.ts +8 -6
- package/src/config.ts +10 -0
- package/src/utils/detect-native-packages.ts +10 -73
- package/src/utils/run-gjs.ts +47 -22
package/lib/commands/build.js
CHANGED
|
@@ -6,11 +6,20 @@ export const buildCommand = {
|
|
|
6
6
|
builder: (yargs) => {
|
|
7
7
|
return yargs
|
|
8
8
|
.option('entry-points', {
|
|
9
|
-
description: "The entry points you want to bundle",
|
|
9
|
+
description: "The entry points you want to bundle. Defaults to bundler.input from package.json#gjsify or .gjsifyrc.js, falling back to src/index.ts when neither is set.",
|
|
10
10
|
array: true,
|
|
11
11
|
type: 'string',
|
|
12
12
|
normalize: true,
|
|
13
|
-
default
|
|
13
|
+
// No yargs `default` here on purpose. A yargs default value
|
|
14
|
+
// is indistinguishable from "user passed the flag" in the
|
|
15
|
+
// parsed args (cliArgs.entryPoints?.length is truthy either
|
|
16
|
+
// way), so the merge step in config.ts would unconditionally
|
|
17
|
+
// overwrite `bundler.input` declared in package.json#gjsify —
|
|
18
|
+
// silently ignoring `gjsify.bundler.input: "src/start.ts"`
|
|
19
|
+
// and producing a bundle from the wrong entry point. The
|
|
20
|
+
// fallback to src/index.ts is applied in config.ts AFTER
|
|
21
|
+
// merging with the cosmiconfig data.
|
|
22
|
+
defaultDescription: "src/index.ts (fallback)",
|
|
14
23
|
coerce: (arg) => {
|
|
15
24
|
// Removes duplicates
|
|
16
25
|
return [...new Set(arg)];
|
|
@@ -43,10 +52,10 @@ export const buildCommand = {
|
|
|
43
52
|
normalize: true,
|
|
44
53
|
})
|
|
45
54
|
.option('minify', {
|
|
46
|
-
description: "
|
|
55
|
+
description: "Minify the bundled output. Defaults to true; use --no-minify to emit pretty-printed code (e.g. for debugging or readable bundle review).",
|
|
47
56
|
type: 'boolean',
|
|
48
57
|
normalize: true,
|
|
49
|
-
|
|
58
|
+
defaultDescription: 'true',
|
|
50
59
|
})
|
|
51
60
|
.option('library', {
|
|
52
61
|
description: "Use this if you want to build a library for Gjsify",
|
package/lib/commands/showcase.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { discoverShowcases, findShowcase } from '../utils/discover-showcases.js';
|
|
2
|
-
import { runMinimalChecks,
|
|
2
|
+
import { runMinimalChecks, detectPackageManager, buildInstallCommand, } from '../utils/check-system-deps.js';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
export const showcaseCommand = {
|
|
@@ -58,12 +58,15 @@ export const showcaseCommand = {
|
|
|
58
58
|
console.error('Run "gjsify showcase" to list available showcases.');
|
|
59
59
|
process.exit(1);
|
|
60
60
|
}
|
|
61
|
-
// System dependency check before delegating — only
|
|
61
|
+
// System dependency check before delegating — only system libs (gjs,
|
|
62
|
+
// gtk4, …). The showcase's npm deps (incl. `@gjsify/webgl` with the
|
|
63
|
+
// gwebgl Vala prebuild) are fetched by `gjsify dlx` into the npm
|
|
64
|
+
// cache, and `runGjsBundle()` picks the prebuild up from the bundle
|
|
65
|
+
// dir via `detectNativePackages()`. Pre-flight-checking npm deps
|
|
66
|
+
// here would fail for `npx @gjsify/cli showcase` (no project
|
|
67
|
+
// node_modules, CLI doesn't dep on the showcase libs).
|
|
62
68
|
const results = runMinimalChecks();
|
|
63
|
-
|
|
64
|
-
results.push(checkGwebgl(process.cwd()));
|
|
65
|
-
}
|
|
66
|
-
const missingHard = results.filter((r) => !r.found && (r.severity === 'required' || r.id === 'gwebgl'));
|
|
69
|
+
const missingHard = results.filter((r) => !r.found && r.severity === 'required');
|
|
67
70
|
if (missingHard.length > 0) {
|
|
68
71
|
console.error('Missing system dependencies:\n');
|
|
69
72
|
for (const dep of missingHard) {
|
package/lib/config.js
CHANGED
|
@@ -214,14 +214,26 @@ export class Config {
|
|
|
214
214
|
const transform = (bundler.transform ??= {});
|
|
215
215
|
if (cliArgs.entryPoints?.length)
|
|
216
216
|
bundler.input = cliArgs.entryPoints;
|
|
217
|
+
// Fallback when neither the CLI flag nor the cosmiconfig data set an
|
|
218
|
+
// entry point. Applied here (post-merge) rather than as a yargs
|
|
219
|
+
// `default:` because yargs defaults are indistinguishable from
|
|
220
|
+
// user-set values, and would silently overwrite `bundler.input`
|
|
221
|
+
// declared in package.json#gjsify.
|
|
222
|
+
if (!bundler.input)
|
|
223
|
+
bundler.input = ['src/index.ts'];
|
|
217
224
|
if (cliArgs.outfile !== undefined)
|
|
218
225
|
output.file = cliArgs.outfile;
|
|
219
226
|
if (cliArgs.outdir !== undefined)
|
|
220
227
|
output.dir = cliArgs.outdir;
|
|
221
228
|
if (cliArgs.format !== undefined)
|
|
222
229
|
output.format = cliArgs.format;
|
|
230
|
+
// CLI flag wins over config; if neither is set, minify by default.
|
|
231
|
+
// Pretty-printed output is opt-in via `--no-minify` or
|
|
232
|
+
// `bundler.output.minify: false` in the config.
|
|
223
233
|
if (cliArgs.minify !== undefined)
|
|
224
234
|
output.minify = cliArgs.minify;
|
|
235
|
+
if (output.minify === undefined)
|
|
236
|
+
output.minify = true;
|
|
225
237
|
if (cliArgs.logLevel) {
|
|
226
238
|
// Map esbuild log levels to Rolldown's narrower set:
|
|
227
239
|
// esbuild → rolldown
|
|
@@ -18,19 +18,6 @@ export interface NativePackage {
|
|
|
18
18
|
* node_modules shadows outer ones), matching Node.js resolution semantics.
|
|
19
19
|
*/
|
|
20
20
|
export declare function detectNativePackages(startDir: string): NativePackage[];
|
|
21
|
-
/**
|
|
22
|
-
* Resolve native packages using Node.js module resolution from a given file path.
|
|
23
|
-
* Reads the nearest package.json to discover dependencies, then checks each
|
|
24
|
-
* for gjsify native prebuilds metadata.
|
|
25
|
-
*
|
|
26
|
-
* Also checks the **nearest package.json itself** — a workspace package may
|
|
27
|
-
* have its own prebuilds (e.g. `@gjsify/webgl` running its own test) and
|
|
28
|
-
* never list itself in dependencies.
|
|
29
|
-
*
|
|
30
|
-
* This complements detectNativePackages() (filesystem walk from CWD) by using
|
|
31
|
-
* require.resolve() — which handles hoisting, workspaces, and nested node_modules.
|
|
32
|
-
*/
|
|
33
|
-
export declare function resolveNativePackages(fromFilePath: string): NativePackage[];
|
|
34
21
|
/**
|
|
35
22
|
* Build the LD_LIBRARY_PATH and GI_TYPELIB_PATH env var values for the detected native packages.
|
|
36
23
|
* Prepends the new paths to any existing values from the environment.
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
// Utility to find npm packages with gjsify native prebuilds.
|
|
2
2
|
// Packages declare: "gjsify": { "prebuilds": "<dir>" } in their package.json.
|
|
3
3
|
// The CLI uses this to auto-set LD_LIBRARY_PATH / GI_TYPELIB_PATH before running gjs.
|
|
4
|
+
//
|
|
5
|
+
// One algorithm — `detectNativePackages(startDir)` — walks up from `startDir`
|
|
6
|
+
// and exhaustively scans every `node_modules` it finds. Used by:
|
|
7
|
+
// * `gjsify run`, `gjsify info`, `gjsify install` — startDir = process.cwd()
|
|
8
|
+
// * `runGjsBundle()` — startDir = dirname(bundlePath), so DLX-cache layouts
|
|
9
|
+
// (`~/.cache/gjsify/dlx/<sha>/.../node_modules/<pkg>/dist/bundle.js`) get
|
|
10
|
+
// their full transitive prebuild set picked up automatically. The
|
|
11
|
+
// transitive walk is what makes `gjsify showcase` / `gjsify dlx` work
|
|
12
|
+
// for packages whose Vala typelibs live in *indirect* deps.
|
|
4
13
|
import { readdirSync, existsSync, readFileSync } from 'node:fs';
|
|
5
|
-
import {
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
-
import { pathToFileURL } from 'node:url';
|
|
14
|
+
import { join, resolve } from 'node:path';
|
|
8
15
|
/** Map Node.js process.arch values to the convention used in prebuilds/ directories. */
|
|
9
16
|
function nodeArchToLinuxArch(arch) {
|
|
10
17
|
const map = {
|
|
@@ -119,77 +126,6 @@ export function detectNativePackages(startDir) {
|
|
|
119
126
|
}
|
|
120
127
|
return merged;
|
|
121
128
|
}
|
|
122
|
-
/** Walk up from dir to find the nearest package.json. */
|
|
123
|
-
function findNearestPackageJson(startDir) {
|
|
124
|
-
let dir = resolve(startDir);
|
|
125
|
-
while (true) {
|
|
126
|
-
const candidate = join(dir, 'package.json');
|
|
127
|
-
if (existsSync(candidate))
|
|
128
|
-
return candidate;
|
|
129
|
-
const parent = resolve(dir, '..');
|
|
130
|
-
if (parent === dir)
|
|
131
|
-
return null;
|
|
132
|
-
dir = parent;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Resolve native packages using Node.js module resolution from a given file path.
|
|
137
|
-
* Reads the nearest package.json to discover dependencies, then checks each
|
|
138
|
-
* for gjsify native prebuilds metadata.
|
|
139
|
-
*
|
|
140
|
-
* Also checks the **nearest package.json itself** — a workspace package may
|
|
141
|
-
* have its own prebuilds (e.g. `@gjsify/webgl` running its own test) and
|
|
142
|
-
* never list itself in dependencies.
|
|
143
|
-
*
|
|
144
|
-
* This complements detectNativePackages() (filesystem walk from CWD) by using
|
|
145
|
-
* require.resolve() — which handles hoisting, workspaces, and nested node_modules.
|
|
146
|
-
*/
|
|
147
|
-
export function resolveNativePackages(fromFilePath) {
|
|
148
|
-
const arch = nodeArchToLinuxArch(process.arch);
|
|
149
|
-
const results = [];
|
|
150
|
-
try {
|
|
151
|
-
const req = createRequire(pathToFileURL(fromFilePath).href);
|
|
152
|
-
// Find the nearest package.json to get the dependency list
|
|
153
|
-
const nearestPkgJson = findNearestPackageJson(dirname(resolve(fromFilePath)));
|
|
154
|
-
if (!nearestPkgJson)
|
|
155
|
-
return results;
|
|
156
|
-
const pkg = readPackageJson(nearestPkgJson);
|
|
157
|
-
if (!pkg)
|
|
158
|
-
return results;
|
|
159
|
-
// Check the nearest package itself (e.g. @gjsify/webgl running its own
|
|
160
|
-
// test bundle — webgl never lists itself in dependencies)
|
|
161
|
-
const ownName = typeof pkg['name'] === 'string' ? pkg['name'] : '';
|
|
162
|
-
if (ownName) {
|
|
163
|
-
const ownNative = checkPackage(dirname(nearestPkgJson), ownName, arch);
|
|
164
|
-
if (ownNative)
|
|
165
|
-
results.push(ownNative);
|
|
166
|
-
}
|
|
167
|
-
const deps = pkg['dependencies'];
|
|
168
|
-
if (!deps)
|
|
169
|
-
return results;
|
|
170
|
-
for (const depName of Object.keys(deps)) {
|
|
171
|
-
try {
|
|
172
|
-
// Resolve the package's main entry, then walk up to find its package.json.
|
|
173
|
-
// We cannot use require.resolve(name + '/package.json') because packages
|
|
174
|
-
// with an "exports" field may not expose ./package.json as a subpath.
|
|
175
|
-
const entryPath = req.resolve(depName);
|
|
176
|
-
const depPkgJson = findNearestPackageJson(dirname(entryPath));
|
|
177
|
-
if (!depPkgJson)
|
|
178
|
-
continue;
|
|
179
|
-
const native = checkPackage(dirname(depPkgJson), depName, arch);
|
|
180
|
-
if (native)
|
|
181
|
-
results.push(native);
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
// Dependency not resolvable — skip
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
// Resolution failed — return empty
|
|
190
|
-
}
|
|
191
|
-
return results;
|
|
192
|
-
}
|
|
193
129
|
/**
|
|
194
130
|
* Build the LD_LIBRARY_PATH and GI_TYPELIB_PATH env var values for the detected native packages.
|
|
195
131
|
* Prepends the new paths to any existing values from the environment.
|
package/lib/utils/run-gjs.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure env computation for a given bundle. Returns the LD_LIBRARY_PATH /
|
|
3
|
+
* GI_TYPELIB_PATH values that {@link runGjsBundle} would inject into the
|
|
4
|
+
* spawned `gjs` process, plus the formatted env-prefix string used for the
|
|
5
|
+
* `$ …` echo.
|
|
6
|
+
*/
|
|
7
|
+
export declare function computeNativeEnvForBundle(bundlePath: string, cwd?: string): {
|
|
8
|
+
env: {
|
|
9
|
+
LD_LIBRARY_PATH: string;
|
|
10
|
+
GI_TYPELIB_PATH: string;
|
|
11
|
+
};
|
|
12
|
+
envPrefix: string;
|
|
13
|
+
};
|
|
1
14
|
/**
|
|
2
15
|
* Run a GJS bundle, automatically setting LD_LIBRARY_PATH and GI_TYPELIB_PATH
|
|
3
|
-
* for any installed native gjsify packages
|
|
4
|
-
*
|
|
5
|
-
* Detection uses two strategies:
|
|
6
|
-
* 1. Filesystem walk from CWD (finds packages in the user's project)
|
|
7
|
-
* 2. require.resolve from the bundle's location (finds packages in the CLI's dependency tree)
|
|
16
|
+
* for any installed native gjsify packages discoverable from either the CWD
|
|
17
|
+
* or the bundle's own node_modules tree.
|
|
8
18
|
*/
|
|
9
19
|
export declare function runGjsBundle(bundlePath: string, extraArgs?: string[]): Promise<void>;
|
package/lib/utils/run-gjs.js
CHANGED
|
@@ -1,29 +1,53 @@
|
|
|
1
1
|
// Shared utility for running a GJS bundle with native package env vars.
|
|
2
|
-
// Used by
|
|
2
|
+
// Used by `gjsify run`, `gjsify dlx`, and the showcase command (via dlx).
|
|
3
|
+
//
|
|
4
|
+
// Detection runs the same exhaustive node_modules walker (`detectNativePackages`)
|
|
5
|
+
// from two starting points and merges by package name (CWD shadows bundle):
|
|
6
|
+
//
|
|
7
|
+
// 1. process.cwd() — picks up native deps in the user's project
|
|
8
|
+
// (yarn / pnpm / npm node_modules walking up).
|
|
9
|
+
// 2. dirname(bundlePath) — picks up native deps in whatever node_modules
|
|
10
|
+
// the bundle lives in. Critical for `gjsify dlx`
|
|
11
|
+
// where the bundle resides in
|
|
12
|
+
// `~/.cache/gjsify/dlx/<sha>/.../node_modules/<pkg>/dist/`
|
|
13
|
+
// and the user's CWD is unrelated. The bundle-side
|
|
14
|
+
// walk also catches transitive deps' typelibs.
|
|
15
|
+
//
|
|
16
|
+
// Env composition is split out as `computeNativeEnvForBundle()` — a pure
|
|
17
|
+
// function that takes a bundle path + cwd and returns the env it would inject.
|
|
18
|
+
// This lets the e2e tests assert the env without spawning gjs.
|
|
3
19
|
import { spawn } from 'node:child_process';
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { detectNativePackages,
|
|
20
|
+
import { dirname, resolve } from 'node:path';
|
|
21
|
+
import { detectNativePackages, buildNativeEnv } from './detect-native-packages.js';
|
|
6
22
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 1. Filesystem walk from CWD (finds packages in the user's project)
|
|
12
|
-
* 2. require.resolve from the bundle's location (finds packages in the CLI's dependency tree)
|
|
23
|
+
* Pure env computation for a given bundle. Returns the LD_LIBRARY_PATH /
|
|
24
|
+
* GI_TYPELIB_PATH values that {@link runGjsBundle} would inject into the
|
|
25
|
+
* spawned `gjs` process, plus the formatted env-prefix string used for the
|
|
26
|
+
* `$ …` echo.
|
|
13
27
|
*/
|
|
14
|
-
export
|
|
15
|
-
const cwd = process.cwd();
|
|
28
|
+
export function computeNativeEnvForBundle(bundlePath, cwd = process.cwd()) {
|
|
16
29
|
const resolvedBundle = resolve(bundlePath);
|
|
17
|
-
// Detect from CWD (filesystem walk) + bundle location (require.resolve)
|
|
18
30
|
const cwdPackages = detectNativePackages(cwd);
|
|
19
|
-
const bundlePackages =
|
|
20
|
-
|
|
21
|
-
const seen = new Set(cwdPackages.map(p => p.name));
|
|
31
|
+
const bundlePackages = detectNativePackages(dirname(resolvedBundle));
|
|
32
|
+
const seen = new Set(cwdPackages.map((p) => p.name));
|
|
22
33
|
const nativePackages = [
|
|
23
34
|
...cwdPackages,
|
|
24
|
-
...bundlePackages.filter(p => !seen.has(p.name)),
|
|
35
|
+
...bundlePackages.filter((p) => !seen.has(p.name)),
|
|
25
36
|
];
|
|
26
|
-
const
|
|
37
|
+
const env = buildNativeEnv(nativePackages);
|
|
38
|
+
const envPrefix = Object.entries(env)
|
|
39
|
+
.filter(([, value]) => value !== undefined && value !== '')
|
|
40
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
41
|
+
.join(' ');
|
|
42
|
+
return { env, envPrefix };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run a GJS bundle, automatically setting LD_LIBRARY_PATH and GI_TYPELIB_PATH
|
|
46
|
+
* for any installed native gjsify packages discoverable from either the CWD
|
|
47
|
+
* or the bundle's own node_modules tree.
|
|
48
|
+
*/
|
|
49
|
+
export async function runGjsBundle(bundlePath, extraArgs = []) {
|
|
50
|
+
const { env: nativeEnv, envPrefix } = computeNativeEnvForBundle(bundlePath);
|
|
27
51
|
const env = {
|
|
28
52
|
...process.env,
|
|
29
53
|
...nativeEnv,
|
|
@@ -32,11 +56,7 @@ export async function runGjsBundle(bundlePath, extraArgs = []) {
|
|
|
32
56
|
// Print the exact command being executed so users can copy-paste it to
|
|
33
57
|
// run gjs directly without the wrapper. Env vars are only shown if we
|
|
34
58
|
// actually set any (i.e. native gjsify packages were detected).
|
|
35
|
-
const
|
|
36
|
-
.filter(([, value]) => value !== undefined && value !== '')
|
|
37
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
38
|
-
.join(' ');
|
|
39
|
-
const gjsCommand = ['gjs', ...gjsArgs.map(a => a.includes(' ') ? `"${a}"` : a)].join(' ');
|
|
59
|
+
const gjsCommand = ['gjs', ...gjsArgs.map((a) => (a.includes(' ') ? `"${a}"` : a))].join(' ');
|
|
40
60
|
console.log(`$ ${envPrefix ? `${envPrefix} ` : ''}${gjsCommand}`);
|
|
41
61
|
const child = spawn('gjs', gjsArgs, { env, stdio: 'inherit' });
|
|
42
62
|
await new Promise((resolvePromise, reject) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "CLI for Gjsify",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
"cli"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@gjsify/create-app": "^0.3.
|
|
27
|
-
"@gjsify/node-polyfills": "^0.3.
|
|
28
|
-
"@gjsify/npm-registry": "^0.3.
|
|
29
|
-
"@gjsify/resolve-npm": "^0.3.
|
|
30
|
-
"@gjsify/rolldown-plugin-gjsify": "^0.3.
|
|
31
|
-
"@gjsify/rolldown-plugin-pnp": "^0.3.
|
|
32
|
-
"@gjsify/semver": "^0.3.
|
|
33
|
-
"@gjsify/tar": "^0.3.
|
|
34
|
-
"@gjsify/web-polyfills": "^0.3.
|
|
26
|
+
"@gjsify/create-app": "^0.3.18",
|
|
27
|
+
"@gjsify/node-polyfills": "^0.3.18",
|
|
28
|
+
"@gjsify/npm-registry": "^0.3.18",
|
|
29
|
+
"@gjsify/resolve-npm": "^0.3.18",
|
|
30
|
+
"@gjsify/rolldown-plugin-gjsify": "^0.3.18",
|
|
31
|
+
"@gjsify/rolldown-plugin-pnp": "^0.3.18",
|
|
32
|
+
"@gjsify/semver": "^0.3.18",
|
|
33
|
+
"@gjsify/tar": "^0.3.18",
|
|
34
|
+
"@gjsify/web-polyfills": "^0.3.18",
|
|
35
35
|
"cosmiconfig": "^9.0.1",
|
|
36
36
|
"get-tsconfig": "^4.14.0",
|
|
37
37
|
"pkg-types": "^2.3.1",
|
|
38
|
-
"rolldown": "^1.0.0
|
|
38
|
+
"rolldown": "^1.0.0",
|
|
39
39
|
"yargs": "^18.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
package/src/commands/build.ts
CHANGED
|
@@ -8,11 +8,20 @@ export const buildCommand: Command<any, CliBuildOptions> = {
|
|
|
8
8
|
builder: (yargs) => {
|
|
9
9
|
return yargs
|
|
10
10
|
.option('entry-points', {
|
|
11
|
-
description: "The entry points you want to bundle",
|
|
11
|
+
description: "The entry points you want to bundle. Defaults to bundler.input from package.json#gjsify or .gjsifyrc.js, falling back to src/index.ts when neither is set.",
|
|
12
12
|
array: true,
|
|
13
13
|
type: 'string',
|
|
14
14
|
normalize: true,
|
|
15
|
-
default
|
|
15
|
+
// No yargs `default` here on purpose. A yargs default value
|
|
16
|
+
// is indistinguishable from "user passed the flag" in the
|
|
17
|
+
// parsed args (cliArgs.entryPoints?.length is truthy either
|
|
18
|
+
// way), so the merge step in config.ts would unconditionally
|
|
19
|
+
// overwrite `bundler.input` declared in package.json#gjsify —
|
|
20
|
+
// silently ignoring `gjsify.bundler.input: "src/start.ts"`
|
|
21
|
+
// and producing a bundle from the wrong entry point. The
|
|
22
|
+
// fallback to src/index.ts is applied in config.ts AFTER
|
|
23
|
+
// merging with the cosmiconfig data.
|
|
24
|
+
defaultDescription: "src/index.ts (fallback)",
|
|
16
25
|
coerce: (arg: string[]) => {
|
|
17
26
|
// Removes duplicates
|
|
18
27
|
return [...new Set(arg)];
|
|
@@ -45,10 +54,10 @@ export const buildCommand: Command<any, CliBuildOptions> = {
|
|
|
45
54
|
normalize: true,
|
|
46
55
|
})
|
|
47
56
|
.option('minify', {
|
|
48
|
-
description: "
|
|
57
|
+
description: "Minify the bundled output. Defaults to true; use --no-minify to emit pretty-printed code (e.g. for debugging or readable bundle review).",
|
|
49
58
|
type: 'boolean',
|
|
50
59
|
normalize: true,
|
|
51
|
-
|
|
60
|
+
defaultDescription: 'true',
|
|
52
61
|
})
|
|
53
62
|
.option('library', {
|
|
54
63
|
description: "Use this if you want to build a library for Gjsify",
|
package/src/commands/showcase.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { Command } from '../types/index.js';
|
|
|
2
2
|
import { discoverShowcases, findShowcase } from '../utils/discover-showcases.js';
|
|
3
3
|
import {
|
|
4
4
|
runMinimalChecks,
|
|
5
|
-
checkGwebgl,
|
|
6
5
|
detectPackageManager,
|
|
7
6
|
buildInstallCommand,
|
|
8
7
|
} from '../utils/check-system-deps.js';
|
|
@@ -79,13 +78,16 @@ export const showcaseCommand: Command<any, ShowcaseOptions> = {
|
|
|
79
78
|
process.exit(1);
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
// System dependency check before delegating — only
|
|
81
|
+
// System dependency check before delegating — only system libs (gjs,
|
|
82
|
+
// gtk4, …). The showcase's npm deps (incl. `@gjsify/webgl` with the
|
|
83
|
+
// gwebgl Vala prebuild) are fetched by `gjsify dlx` into the npm
|
|
84
|
+
// cache, and `runGjsBundle()` picks the prebuild up from the bundle
|
|
85
|
+
// dir via `detectNativePackages()`. Pre-flight-checking npm deps
|
|
86
|
+
// here would fail for `npx @gjsify/cli showcase` (no project
|
|
87
|
+
// node_modules, CLI doesn't dep on the showcase libs).
|
|
83
88
|
const results = runMinimalChecks();
|
|
84
|
-
if (showcase.needsWebgl) {
|
|
85
|
-
results.push(checkGwebgl(process.cwd()));
|
|
86
|
-
}
|
|
87
89
|
const missingHard = results.filter(
|
|
88
|
-
(r) => !r.found &&
|
|
90
|
+
(r) => !r.found && r.severity === 'required',
|
|
89
91
|
);
|
|
90
92
|
if (missingHard.length > 0) {
|
|
91
93
|
console.error('Missing system dependencies:\n');
|
package/src/config.ts
CHANGED
|
@@ -231,10 +231,20 @@ export class Config {
|
|
|
231
231
|
const transform = (bundler.transform ??= {});
|
|
232
232
|
|
|
233
233
|
if (cliArgs.entryPoints?.length) bundler.input = cliArgs.entryPoints;
|
|
234
|
+
// Fallback when neither the CLI flag nor the cosmiconfig data set an
|
|
235
|
+
// entry point. Applied here (post-merge) rather than as a yargs
|
|
236
|
+
// `default:` because yargs defaults are indistinguishable from
|
|
237
|
+
// user-set values, and would silently overwrite `bundler.input`
|
|
238
|
+
// declared in package.json#gjsify.
|
|
239
|
+
if (!bundler.input) bundler.input = ['src/index.ts'];
|
|
234
240
|
if (cliArgs.outfile !== undefined) output.file = cliArgs.outfile;
|
|
235
241
|
if (cliArgs.outdir !== undefined) output.dir = cliArgs.outdir;
|
|
236
242
|
if (cliArgs.format !== undefined) output.format = cliArgs.format as 'esm' | 'cjs' | 'iife';
|
|
243
|
+
// CLI flag wins over config; if neither is set, minify by default.
|
|
244
|
+
// Pretty-printed output is opt-in via `--no-minify` or
|
|
245
|
+
// `bundler.output.minify: false` in the config.
|
|
237
246
|
if (cliArgs.minify !== undefined) output.minify = cliArgs.minify;
|
|
247
|
+
if (output.minify === undefined) output.minify = true;
|
|
238
248
|
if (cliArgs.logLevel) {
|
|
239
249
|
// Map esbuild log levels to Rolldown's narrower set:
|
|
240
250
|
// esbuild → rolldown
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
// Utility to find npm packages with gjsify native prebuilds.
|
|
2
2
|
// Packages declare: "gjsify": { "prebuilds": "<dir>" } in their package.json.
|
|
3
3
|
// The CLI uses this to auto-set LD_LIBRARY_PATH / GI_TYPELIB_PATH before running gjs.
|
|
4
|
+
//
|
|
5
|
+
// One algorithm — `detectNativePackages(startDir)` — walks up from `startDir`
|
|
6
|
+
// and exhaustively scans every `node_modules` it finds. Used by:
|
|
7
|
+
// * `gjsify run`, `gjsify info`, `gjsify install` — startDir = process.cwd()
|
|
8
|
+
// * `runGjsBundle()` — startDir = dirname(bundlePath), so DLX-cache layouts
|
|
9
|
+
// (`~/.cache/gjsify/dlx/<sha>/.../node_modules/<pkg>/dist/bundle.js`) get
|
|
10
|
+
// their full transitive prebuild set picked up automatically. The
|
|
11
|
+
// transitive walk is what makes `gjsify showcase` / `gjsify dlx` work
|
|
12
|
+
// for packages whose Vala typelibs live in *indirect* deps.
|
|
4
13
|
|
|
5
14
|
import { readdirSync, existsSync, readFileSync } from 'node:fs';
|
|
6
|
-
import {
|
|
7
|
-
import { dirname, join, resolve } from 'node:path';
|
|
8
|
-
import { pathToFileURL } from 'node:url';
|
|
15
|
+
import { join, resolve } from 'node:path';
|
|
9
16
|
|
|
10
17
|
export interface NativePackage {
|
|
11
18
|
/** npm package name, e.g. "@gjsify/webgl" */
|
|
@@ -129,76 +136,6 @@ export function detectNativePackages(startDir: string): NativePackage[] {
|
|
|
129
136
|
return merged;
|
|
130
137
|
}
|
|
131
138
|
|
|
132
|
-
/** Walk up from dir to find the nearest package.json. */
|
|
133
|
-
function findNearestPackageJson(startDir: string): string | null {
|
|
134
|
-
let dir = resolve(startDir);
|
|
135
|
-
while (true) {
|
|
136
|
-
const candidate = join(dir, 'package.json');
|
|
137
|
-
if (existsSync(candidate)) return candidate;
|
|
138
|
-
const parent = resolve(dir, '..');
|
|
139
|
-
if (parent === dir) return null;
|
|
140
|
-
dir = parent;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Resolve native packages using Node.js module resolution from a given file path.
|
|
146
|
-
* Reads the nearest package.json to discover dependencies, then checks each
|
|
147
|
-
* for gjsify native prebuilds metadata.
|
|
148
|
-
*
|
|
149
|
-
* Also checks the **nearest package.json itself** — a workspace package may
|
|
150
|
-
* have its own prebuilds (e.g. `@gjsify/webgl` running its own test) and
|
|
151
|
-
* never list itself in dependencies.
|
|
152
|
-
*
|
|
153
|
-
* This complements detectNativePackages() (filesystem walk from CWD) by using
|
|
154
|
-
* require.resolve() — which handles hoisting, workspaces, and nested node_modules.
|
|
155
|
-
*/
|
|
156
|
-
export function resolveNativePackages(fromFilePath: string): NativePackage[] {
|
|
157
|
-
const arch = nodeArchToLinuxArch(process.arch);
|
|
158
|
-
const results: NativePackage[] = [];
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const req = createRequire(pathToFileURL(fromFilePath).href);
|
|
162
|
-
|
|
163
|
-
// Find the nearest package.json to get the dependency list
|
|
164
|
-
const nearestPkgJson = findNearestPackageJson(dirname(resolve(fromFilePath)));
|
|
165
|
-
if (!nearestPkgJson) return results;
|
|
166
|
-
|
|
167
|
-
const pkg = readPackageJson(nearestPkgJson);
|
|
168
|
-
if (!pkg) return results;
|
|
169
|
-
|
|
170
|
-
// Check the nearest package itself (e.g. @gjsify/webgl running its own
|
|
171
|
-
// test bundle — webgl never lists itself in dependencies)
|
|
172
|
-
const ownName = typeof pkg['name'] === 'string' ? pkg['name'] as string : '';
|
|
173
|
-
if (ownName) {
|
|
174
|
-
const ownNative = checkPackage(dirname(nearestPkgJson), ownName, arch);
|
|
175
|
-
if (ownNative) results.push(ownNative);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const deps = pkg['dependencies'] as Record<string, string> | undefined;
|
|
179
|
-
if (!deps) return results;
|
|
180
|
-
|
|
181
|
-
for (const depName of Object.keys(deps)) {
|
|
182
|
-
try {
|
|
183
|
-
// Resolve the package's main entry, then walk up to find its package.json.
|
|
184
|
-
// We cannot use require.resolve(name + '/package.json') because packages
|
|
185
|
-
// with an "exports" field may not expose ./package.json as a subpath.
|
|
186
|
-
const entryPath = req.resolve(depName);
|
|
187
|
-
const depPkgJson = findNearestPackageJson(dirname(entryPath));
|
|
188
|
-
if (!depPkgJson) continue;
|
|
189
|
-
const native = checkPackage(dirname(depPkgJson), depName, arch);
|
|
190
|
-
if (native) results.push(native);
|
|
191
|
-
} catch {
|
|
192
|
-
// Dependency not resolvable — skip
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
} catch {
|
|
196
|
-
// Resolution failed — return empty
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return results;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
139
|
/**
|
|
203
140
|
* Build the LD_LIBRARY_PATH and GI_TYPELIB_PATH env var values for the detected native packages.
|
|
204
141
|
* Prepends the new paths to any existing values from the environment.
|
package/src/utils/run-gjs.ts
CHANGED
|
@@ -1,34 +1,63 @@
|
|
|
1
1
|
// Shared utility for running a GJS bundle with native package env vars.
|
|
2
|
-
// Used by
|
|
2
|
+
// Used by `gjsify run`, `gjsify dlx`, and the showcase command (via dlx).
|
|
3
|
+
//
|
|
4
|
+
// Detection runs the same exhaustive node_modules walker (`detectNativePackages`)
|
|
5
|
+
// from two starting points and merges by package name (CWD shadows bundle):
|
|
6
|
+
//
|
|
7
|
+
// 1. process.cwd() — picks up native deps in the user's project
|
|
8
|
+
// (yarn / pnpm / npm node_modules walking up).
|
|
9
|
+
// 2. dirname(bundlePath) — picks up native deps in whatever node_modules
|
|
10
|
+
// the bundle lives in. Critical for `gjsify dlx`
|
|
11
|
+
// where the bundle resides in
|
|
12
|
+
// `~/.cache/gjsify/dlx/<sha>/.../node_modules/<pkg>/dist/`
|
|
13
|
+
// and the user's CWD is unrelated. The bundle-side
|
|
14
|
+
// walk also catches transitive deps' typelibs.
|
|
15
|
+
//
|
|
16
|
+
// Env composition is split out as `computeNativeEnvForBundle()` — a pure
|
|
17
|
+
// function that takes a bundle path + cwd and returns the env it would inject.
|
|
18
|
+
// This lets the e2e tests assert the env without spawning gjs.
|
|
3
19
|
|
|
4
20
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
-
import { detectNativePackages,
|
|
21
|
+
import { dirname, resolve } from 'node:path';
|
|
22
|
+
import { detectNativePackages, buildNativeEnv } from './detect-native-packages.js';
|
|
7
23
|
|
|
8
24
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* 1. Filesystem walk from CWD (finds packages in the user's project)
|
|
14
|
-
* 2. require.resolve from the bundle's location (finds packages in the CLI's dependency tree)
|
|
25
|
+
* Pure env computation for a given bundle. Returns the LD_LIBRARY_PATH /
|
|
26
|
+
* GI_TYPELIB_PATH values that {@link runGjsBundle} would inject into the
|
|
27
|
+
* spawned `gjs` process, plus the formatted env-prefix string used for the
|
|
28
|
+
* `$ …` echo.
|
|
15
29
|
*/
|
|
16
|
-
export
|
|
17
|
-
|
|
30
|
+
export function computeNativeEnvForBundle(
|
|
31
|
+
bundlePath: string,
|
|
32
|
+
cwd: string = process.cwd(),
|
|
33
|
+
): { env: { LD_LIBRARY_PATH: string; GI_TYPELIB_PATH: string }; envPrefix: string } {
|
|
18
34
|
const resolvedBundle = resolve(bundlePath);
|
|
19
35
|
|
|
20
|
-
// Detect from CWD (filesystem walk) + bundle location (require.resolve)
|
|
21
36
|
const cwdPackages = detectNativePackages(cwd);
|
|
22
|
-
const bundlePackages =
|
|
37
|
+
const bundlePackages = detectNativePackages(dirname(resolvedBundle));
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
const seen = new Set(cwdPackages.map(p => p.name));
|
|
39
|
+
const seen = new Set(cwdPackages.map((p) => p.name));
|
|
26
40
|
const nativePackages = [
|
|
27
41
|
...cwdPackages,
|
|
28
|
-
...bundlePackages.filter(p => !seen.has(p.name)),
|
|
42
|
+
...bundlePackages.filter((p) => !seen.has(p.name)),
|
|
29
43
|
];
|
|
30
44
|
|
|
31
|
-
const
|
|
45
|
+
const env = buildNativeEnv(nativePackages);
|
|
46
|
+
const envPrefix = Object.entries(env)
|
|
47
|
+
.filter(([, value]) => value !== undefined && value !== '')
|
|
48
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
49
|
+
.join(' ');
|
|
50
|
+
|
|
51
|
+
return { env, envPrefix };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run a GJS bundle, automatically setting LD_LIBRARY_PATH and GI_TYPELIB_PATH
|
|
56
|
+
* for any installed native gjsify packages discoverable from either the CWD
|
|
57
|
+
* or the bundle's own node_modules tree.
|
|
58
|
+
*/
|
|
59
|
+
export async function runGjsBundle(bundlePath: string, extraArgs: string[] = []): Promise<void> {
|
|
60
|
+
const { env: nativeEnv, envPrefix } = computeNativeEnvForBundle(bundlePath);
|
|
32
61
|
|
|
33
62
|
const env = {
|
|
34
63
|
...process.env,
|
|
@@ -40,11 +69,7 @@ export async function runGjsBundle(bundlePath: string, extraArgs: string[] = [])
|
|
|
40
69
|
// Print the exact command being executed so users can copy-paste it to
|
|
41
70
|
// run gjs directly without the wrapper. Env vars are only shown if we
|
|
42
71
|
// actually set any (i.e. native gjsify packages were detected).
|
|
43
|
-
const
|
|
44
|
-
.filter(([, value]) => value !== undefined && value !== '')
|
|
45
|
-
.map(([key, value]) => `${key}=${value}`)
|
|
46
|
-
.join(' ');
|
|
47
|
-
const gjsCommand = ['gjs', ...gjsArgs.map(a => a.includes(' ') ? `"${a}"` : a)].join(' ');
|
|
72
|
+
const gjsCommand = ['gjs', ...gjsArgs.map((a) => (a.includes(' ') ? `"${a}"` : a))].join(' ');
|
|
48
73
|
console.log(`$ ${envPrefix ? `${envPrefix} ` : ''}${gjsCommand}`);
|
|
49
74
|
|
|
50
75
|
const child = spawn('gjs', gjsArgs, { env, stdio: 'inherit' });
|