@gjsify/cli 0.3.4 → 0.3.6
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/actions/build.js +14 -5
- package/lib/commands/build.js +2 -3
- package/lib/commands/dlx.d.ts +11 -0
- package/lib/commands/dlx.js +117 -0
- package/lib/commands/index.d.ts +2 -0
- package/lib/commands/index.js +2 -0
- package/lib/commands/install.d.ts +10 -0
- package/lib/commands/install.js +99 -0
- package/lib/commands/showcase.js +46 -34
- package/lib/index.js +3 -1
- package/lib/utils/check-system-deps.js +17 -6
- package/lib/utils/discover-showcases.d.ts +6 -5
- package/lib/utils/discover-showcases.js +28 -50
- package/lib/utils/dlx-cache.d.ts +34 -0
- package/lib/utils/dlx-cache.js +120 -0
- package/lib/utils/install-backend.d.ts +11 -0
- package/lib/utils/install-backend.js +57 -0
- package/lib/utils/parse-spec.d.ts +20 -0
- package/lib/utils/parse-spec.js +37 -0
- package/lib/utils/resolve-gjs-entry.d.ts +7 -0
- package/lib/utils/resolve-gjs-entry.js +65 -0
- package/package.json +5 -10
- package/showcases.json +40 -0
- package/src/actions/build.ts +14 -5
- package/src/commands/build.ts +2 -3
- package/src/commands/dlx.ts +173 -0
- package/src/commands/index.ts +3 -1
- package/src/commands/install.ts +116 -0
- package/src/commands/showcase.ts +40 -21
- package/src/index.ts +4 -0
- package/src/utils/check-system-deps.ts +17 -6
- package/src/utils/discover-showcases.ts +42 -53
- package/src/utils/dlx-cache.ts +135 -0
- package/src/utils/install-backend.ts +79 -0
- package/src/utils/parse-spec.ts +48 -0
- package/src/utils/resolve-gjs-entry.ts +96 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Parse a `gjsify dlx` package spec — distinguishes local paths from npm specs.
|
|
2
|
+
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export type ParsedSpec =
|
|
7
|
+
| { kind: 'local'; path: string }
|
|
8
|
+
| { kind: 'registry'; name: string; version?: string; spec: string };
|
|
9
|
+
|
|
10
|
+
const NPM_NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse a CLI input into either a local-path or an npm-registry spec.
|
|
14
|
+
*
|
|
15
|
+
* Local: starts with `./`, `../`, `/`, or is an existing directory path.
|
|
16
|
+
* Registry: `<name>` | `<name>@<version>` | `@scope/<name>` | `@scope/<name>@<version>`
|
|
17
|
+
*
|
|
18
|
+
* The full spec string is preserved on the registry case so it can be passed
|
|
19
|
+
* verbatim to `npm install` (which already understands dist-tags, ranges,
|
|
20
|
+
* git URIs, tarball URLs, etc. — we don't re-parse those).
|
|
21
|
+
*/
|
|
22
|
+
export function parseSpec(input: string): ParsedSpec {
|
|
23
|
+
if (!input) throw new Error('dlx: empty package spec');
|
|
24
|
+
|
|
25
|
+
if (input.startsWith('./') || input.startsWith('../') || isAbsolute(input)) {
|
|
26
|
+
return { kind: 'local', path: resolve(input) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (existsSync(input)) {
|
|
30
|
+
return { kind: 'local', path: resolve(input) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Registry spec: split off the version after the LAST `@` that isn't the
|
|
34
|
+
// leading scope separator.
|
|
35
|
+
let name = input;
|
|
36
|
+
let version: string | undefined;
|
|
37
|
+
const lastAt = input.lastIndexOf('@');
|
|
38
|
+
if (lastAt > 0) {
|
|
39
|
+
name = input.slice(0, lastAt);
|
|
40
|
+
version = input.slice(lastAt + 1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!NPM_NAME_RE.test(name)) {
|
|
44
|
+
throw new Error(`dlx: invalid package name "${name}"`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { kind: 'registry', name, version, spec: input };
|
|
48
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Resolve the GJS entry point of an installed package.
|
|
2
|
+
//
|
|
3
|
+
// Per the `gjsify` field convention (see CLI reference):
|
|
4
|
+
//
|
|
5
|
+
// {
|
|
6
|
+
// "gjsify": {
|
|
7
|
+
// "main": "dist/gjs.js",
|
|
8
|
+
// "bin": { "name-a": "dist/a.js", "name-b": "dist/b.js" }
|
|
9
|
+
// }
|
|
10
|
+
// }
|
|
11
|
+
//
|
|
12
|
+
// Resolution order:
|
|
13
|
+
// 1. user-supplied bin name + `gjsify.bin[name]` → that path
|
|
14
|
+
// 2. single-entry `gjsify.bin` → the only path
|
|
15
|
+
// 3. `gjsify.main` → that path
|
|
16
|
+
// 4. fallback: `package.json#main` → that path (advisory warning)
|
|
17
|
+
// 5. otherwise: hard-fail with a fix hint
|
|
18
|
+
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { existsSync } from 'node:fs';
|
|
21
|
+
import { join, resolve } from 'node:path';
|
|
22
|
+
|
|
23
|
+
interface ResolvedEntry {
|
|
24
|
+
bundlePath: string;
|
|
25
|
+
binName: string | null;
|
|
26
|
+
fromFallback: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface PackageJson {
|
|
30
|
+
name?: string;
|
|
31
|
+
main?: string;
|
|
32
|
+
gjsify?: {
|
|
33
|
+
main?: string;
|
|
34
|
+
bin?: Record<string, string>;
|
|
35
|
+
prebuilds?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolveGjsEntry(
|
|
40
|
+
pkgDir: string,
|
|
41
|
+
binName: string | null,
|
|
42
|
+
): ResolvedEntry {
|
|
43
|
+
const pkgJsonPath = join(pkgDir, 'package.json');
|
|
44
|
+
if (!existsSync(pkgJsonPath)) {
|
|
45
|
+
throw new Error(`dlx: no package.json found at ${pkgDir}`);
|
|
46
|
+
}
|
|
47
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as PackageJson;
|
|
48
|
+
|
|
49
|
+
const gjsifyBin = pkg.gjsify?.bin;
|
|
50
|
+
const gjsifyMain = pkg.gjsify?.main;
|
|
51
|
+
const fallbackMain = pkg.main;
|
|
52
|
+
|
|
53
|
+
let entry: string | undefined;
|
|
54
|
+
let resolvedBin: string | null = null;
|
|
55
|
+
let fromFallback = false;
|
|
56
|
+
|
|
57
|
+
if (binName !== null) {
|
|
58
|
+
if (!gjsifyBin || !gjsifyBin[binName]) {
|
|
59
|
+
const known = gjsifyBin ? Object.keys(gjsifyBin).join(', ') : '(none)';
|
|
60
|
+
throw new Error(
|
|
61
|
+
`dlx: package "${pkg.name ?? pkgDir}" has no GJS bin named "${binName}" — known: ${known}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
entry = gjsifyBin[binName];
|
|
65
|
+
resolvedBin = binName;
|
|
66
|
+
} else if (gjsifyBin && Object.keys(gjsifyBin).length === 1) {
|
|
67
|
+
const onlyBin = Object.keys(gjsifyBin)[0];
|
|
68
|
+
entry = gjsifyBin[onlyBin];
|
|
69
|
+
resolvedBin = onlyBin;
|
|
70
|
+
} else if (gjsifyMain) {
|
|
71
|
+
entry = gjsifyMain;
|
|
72
|
+
} else if (fallbackMain) {
|
|
73
|
+
entry = fallbackMain;
|
|
74
|
+
fromFallback = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (gjsifyBin && Object.keys(gjsifyBin).length > 1 && binName === null) {
|
|
78
|
+
const names = Object.keys(gjsifyBin).join(', ');
|
|
79
|
+
throw new Error(
|
|
80
|
+
`dlx: package "${pkg.name ?? pkgDir}" defines multiple GJS bins — pass one of: ${names}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!entry) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`dlx: package "${pkg.name ?? pkgDir}" has no GJS entry — set \`gjsify.main\` (or \`gjsify.bin\`) in its package.json`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const bundlePath = resolve(pkgDir, entry);
|
|
91
|
+
if (!existsSync(bundlePath)) {
|
|
92
|
+
throw new Error(`dlx: GJS entry not found: ${bundlePath}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { bundlePath, binName: resolvedBin, fromFallback };
|
|
96
|
+
}
|