@ts-for-gir/cli 4.0.0-rc.9 → 4.0.1
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/README.md +43 -3
- package/bin/ts-for-gir +454 -129
- package/bin/ts-for-gir-gjs +1213 -0
- package/dist-templates/types-gjsify/README.md +32 -0
- package/dist-templates/types-gjsify/biome.json +38 -0
- package/dist-templates/types-gjsify/main.ts +19 -0
- package/dist-templates/types-gjsify/package.json +26 -0
- package/dist-templates/types-gjsify/tsconfig.json +15 -0
- package/dist-templates/types-locally/package.json +1 -1
- package/dist-templates/types-npm/package.json +5 -5
- package/dist-templates/types-workspace/package.json +1 -1
- package/package.json +194 -83
- package/src/commands/create.ts +84 -12
- package/src/commands/index.ts +1 -0
- package/src/commands/self-update.ts +142 -0
- package/src/config/config-loader.ts +6 -2
- package/src/config/defaults.ts +14 -0
- package/src/config/options.ts +2 -2
- package/src/start.ts +20 -6
- package/src/types/command-args.ts +8 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything you need for the `ts-for-gir self-update` command is located here
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { chmodSync, existsSync, renameSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
import { APP_NAME, APP_VERSION } from "@ts-for-gir/lib";
|
|
10
|
+
|
|
11
|
+
import type { SelfUpdateCommandArgs } from "../types/index.ts";
|
|
12
|
+
|
|
13
|
+
const REPO = "gjsify/ts-for-gir";
|
|
14
|
+
const GITHUB_API = "https://api.github.com";
|
|
15
|
+
const GJS_ASSET_NAME = "ts-for-gir-gjs";
|
|
16
|
+
|
|
17
|
+
function getCurrentBinaryPath(): string | null {
|
|
18
|
+
const p = process.argv[1] ?? null;
|
|
19
|
+
if (!p) return null;
|
|
20
|
+
// Refuse to update in dev mode (source file or node_modules path)
|
|
21
|
+
if (p.endsWith(".ts") || p.includes("node_modules")) return null;
|
|
22
|
+
return p;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function fetchJson(url: string): Promise<unknown> {
|
|
26
|
+
const headers: Record<string, string> = {
|
|
27
|
+
Accept: "application/vnd.github.v3+json",
|
|
28
|
+
"User-Agent": `ts-for-gir/${APP_VERSION}`,
|
|
29
|
+
};
|
|
30
|
+
const token = process.env.GITHUB_TOKEN;
|
|
31
|
+
if (token) headers.Authorization = `token ${token}`;
|
|
32
|
+
|
|
33
|
+
const response = await fetch(url, { headers });
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`HTTP ${response.status} from ${url}`);
|
|
36
|
+
}
|
|
37
|
+
return response.json();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function downloadBinary(url: string, destPath: string): Promise<void> {
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
headers: { "User-Agent": `ts-for-gir/${APP_VERSION}` },
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`HTTP ${response.status} downloading binary`);
|
|
46
|
+
}
|
|
47
|
+
const buffer = await response.arrayBuffer();
|
|
48
|
+
const tmpPath = join(tmpdir(), `ts-for-gir-update-${Date.now()}`);
|
|
49
|
+
writeFileSync(tmpPath, Buffer.from(buffer));
|
|
50
|
+
chmodSync(tmpPath, 0o755);
|
|
51
|
+
renameSync(tmpPath, destPath); // atomic on POSIX
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const command = "self-update";
|
|
55
|
+
const description = "Update ts-for-gir to the latest version from GitHub releases";
|
|
56
|
+
const examples: ReadonlyArray<[string, string?]> = [
|
|
57
|
+
[`${APP_NAME} self-update`, "Check for updates and install the latest version"],
|
|
58
|
+
[`${APP_NAME} self-update --check`, "Only check for a newer version, do not install"],
|
|
59
|
+
[`${APP_NAME} self-update --force`, "Force reinstall even if already on the latest version"],
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const builder = (yargs: import("yargs").Argv) =>
|
|
63
|
+
yargs
|
|
64
|
+
.option("check", {
|
|
65
|
+
description: "Only check for a newer version, do not install",
|
|
66
|
+
type: "boolean",
|
|
67
|
+
default: false,
|
|
68
|
+
})
|
|
69
|
+
.option("force", {
|
|
70
|
+
description: "Force reinstall even if already on the latest version",
|
|
71
|
+
type: "boolean",
|
|
72
|
+
default: false,
|
|
73
|
+
})
|
|
74
|
+
.example(examples as [string, string?][]);
|
|
75
|
+
|
|
76
|
+
const handler = async (args: SelfUpdateCommandArgs): Promise<void> => {
|
|
77
|
+
console.log(`Checking for updates... (current: v${APP_VERSION})`);
|
|
78
|
+
|
|
79
|
+
let release: Record<string, unknown>;
|
|
80
|
+
try {
|
|
81
|
+
release = (await fetchJson(`${GITHUB_API}/repos/${REPO}/releases/latest`)) as Record<string, unknown>;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
84
|
+
process.stderr.write(`Failed to fetch release information: ${msg}\n`);
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const latestVersion = (release.tag_name as string).replace(/^v/, "");
|
|
90
|
+
|
|
91
|
+
if (latestVersion === APP_VERSION && !args.force) {
|
|
92
|
+
console.log(`Already up to date (v${APP_VERSION})`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (args.check) {
|
|
97
|
+
console.log(`New version available: v${latestVersion} (current: v${APP_VERSION})`);
|
|
98
|
+
console.log(`Run \`${APP_NAME} self-update\` to install it.`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`Updating to v${latestVersion}...`);
|
|
103
|
+
|
|
104
|
+
const assets = release.assets as Array<Record<string, string>>;
|
|
105
|
+
const asset = assets.find((a) => a.name === GJS_ASSET_NAME);
|
|
106
|
+
if (!asset) {
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
`No GJS binary found in release ${release.tag_name}.\n` +
|
|
109
|
+
"self-update requires the GJS bundle to be installed via install.js.\n" +
|
|
110
|
+
"For npm installations use: npm update -g @ts-for-gir/cli\n",
|
|
111
|
+
);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const currentPath = getCurrentBinaryPath();
|
|
117
|
+
if (!currentPath || !existsSync(currentPath)) {
|
|
118
|
+
process.stderr.write(
|
|
119
|
+
"Cannot determine current binary path for update.\n" +
|
|
120
|
+
"self-update only works when running the installed GJS binary.\n",
|
|
121
|
+
);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await downloadBinary(asset.browser_download_url, currentPath);
|
|
128
|
+
console.log(`Successfully updated to v${latestVersion}`);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
131
|
+
process.stderr.write(`Update failed: ${msg}\n`);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const selfUpdate = {
|
|
137
|
+
command,
|
|
138
|
+
description,
|
|
139
|
+
builder,
|
|
140
|
+
handler,
|
|
141
|
+
examples,
|
|
142
|
+
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
6
7
|
import type { ConfigFlags, OptionsGeneration, UserConfig, UserConfigLoadResult } from "@ts-for-gir/lib";
|
|
7
8
|
import { APP_NAME, isEqual } from "@ts-for-gir/lib";
|
|
8
9
|
import { type Options as ConfigSearchOptions, cosmiconfig } from "cosmiconfig";
|
|
@@ -18,9 +19,12 @@ import { docOptions, options } from "./options.ts";
|
|
|
18
19
|
export async function loadConfigFile(configName?: string): Promise<UserConfigLoadResult | null> {
|
|
19
20
|
const configSearchOptions: Partial<ConfigSearchOptions> = {
|
|
20
21
|
loaders: {
|
|
21
|
-
// ESM loader
|
|
22
|
+
// ESM loader. cosmiconfig hands us an absolute filesystem path; Node's import()
|
|
23
|
+
// tolerates that as a non-spec extension, but spec-compliant runtimes (GJS /
|
|
24
|
+
// SpiderMonkey) reject it with "Module not found: <abs-path>". Convert to a
|
|
25
|
+
// file:// URL so the loader works in both runtimes.
|
|
22
26
|
".js": async (filepath) => {
|
|
23
|
-
const file = await import(filepath);
|
|
27
|
+
const file = await import(pathToFileURL(filepath).href);
|
|
24
28
|
|
|
25
29
|
// Files with `exports.default = { ... }`
|
|
26
30
|
if (file?.default?.default) {
|
package/src/config/defaults.ts
CHANGED
|
@@ -53,6 +53,20 @@ function getDefaultGirDirectories(): string[] {
|
|
|
53
53
|
"/usr/lib/x86_64-linux-gnu/mutter-*",
|
|
54
54
|
];
|
|
55
55
|
|
|
56
|
+
// Flatpak: `--filesystem=host` exposes the host's filesystem under
|
|
57
|
+
// /run/host. The GNOME runtime ships GIR typelibs but not the XML, so
|
|
58
|
+
// inside the sandbox we must read from /run/host/usr/share/gir-1.0.
|
|
59
|
+
// Detected via FLATPAK_ID (set for every Flatpak-launched process) or
|
|
60
|
+
// the .flatpak-info marker file (always present in the sandbox).
|
|
61
|
+
if (process.env.FLATPAK_ID || existsSync("/.flatpak-info")) {
|
|
62
|
+
girDirectories.unshift(
|
|
63
|
+
"/run/host/usr/local/share/gir-1.0",
|
|
64
|
+
"/run/host/usr/share/gir-1.0",
|
|
65
|
+
"/run/host/usr/share/*/gir-1.0",
|
|
66
|
+
"/run/host/usr/lib/x86_64-linux-gnu/mutter-*",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
56
70
|
// NixOS and other distributions does not have a /usr/local/share directory.
|
|
57
71
|
// Instead, the nix store paths with Gir files are set as XDG_DATA_DIRS.
|
|
58
72
|
// See https://github.com/NixOS/nixpkgs/blob/96e18717904dfedcd884541e5a92bf9ff632cf39/pkgs/development/libraries/gobject-introspection/setup-hook.sh#L7-L10
|
package/src/config/options.ts
CHANGED
|
@@ -257,8 +257,8 @@ export const createOptions = {
|
|
|
257
257
|
template: {
|
|
258
258
|
type: "string" as const,
|
|
259
259
|
alias: "t",
|
|
260
|
-
description: "Template to scaffold (types-locally, types-npm, types-workspace)",
|
|
261
|
-
choices: ["types-locally", "types-npm", "types-workspace"] as const,
|
|
260
|
+
description: "Template to scaffold (types-locally, types-npm, types-workspace, types-gjsify)",
|
|
261
|
+
choices: ["types-locally", "types-npm", "types-workspace", "types-gjsify"] as const,
|
|
262
262
|
},
|
|
263
263
|
install: {
|
|
264
264
|
type: "boolean" as const,
|
package/src/start.ts
CHANGED
|
@@ -2,18 +2,30 @@ import { APP_NAME, APP_USAGE, APP_VERSION } from "@ts-for-gir/lib";
|
|
|
2
2
|
import yargs, { type CommandModule } from "yargs";
|
|
3
3
|
import { hideBin } from "yargs/helpers";
|
|
4
4
|
|
|
5
|
-
import { analyze, copy, create, doc, generate, json, list } from "./commands/index.ts";
|
|
5
|
+
import { analyze, copy, create, doc, generate, json, list, selfUpdate } from "./commands/index.ts";
|
|
6
6
|
|
|
7
7
|
try {
|
|
8
|
-
|
|
8
|
+
const cli = yargs(hideBin(process.argv));
|
|
9
|
+
await cli
|
|
9
10
|
.scriptName(APP_NAME)
|
|
10
11
|
.strict()
|
|
11
12
|
.usage(APP_USAGE)
|
|
12
13
|
.version(APP_VERSION)
|
|
14
|
+
// Use the full terminal width for help. yargs's default caps at 80
|
|
15
|
+
// (`Math.min(80, process.stdout.columns)`); we explicitly opt into
|
|
16
|
+
// the real terminal width so long option/description lines wrap at
|
|
17
|
+
// the actual terminal edge instead of an arbitrary 80-col limit.
|
|
18
|
+
// Under GJS, `process.stdout.columns` is backed by
|
|
19
|
+
// `@gjsify/terminal-native` (ioctl TIOCGWINSZ) when the typelib is
|
|
20
|
+
// on `GI_TYPELIB_PATH` — the install.js launcher imports those env
|
|
21
|
+
// vars from the gjsify global install when present.
|
|
22
|
+
.wrap(cli.terminalWidth())
|
|
13
23
|
// Disable yargs's internal `process.exit` and route both success
|
|
14
|
-
// and failure through `parseAsync` + `process.
|
|
15
|
-
// command handlers complete and stdout drains before the
|
|
16
|
-
// (Node or the gjsify Node-compat loader on GJS
|
|
24
|
+
// and failure through `parseAsync` + an explicit `process.exit` so
|
|
25
|
+
// async command handlers complete and stdout drains before the
|
|
26
|
+
// runtime (Node or the gjsify Node-compat loader on GJS, which
|
|
27
|
+
// keeps a GLib main loop alive that would otherwise prevent the
|
|
28
|
+
// process from exiting after main() returns) tears down.
|
|
17
29
|
.exitProcess(false)
|
|
18
30
|
.fail(false)
|
|
19
31
|
// TODO: Fix this
|
|
@@ -24,11 +36,13 @@ try {
|
|
|
24
36
|
.command(list as unknown as CommandModule)
|
|
25
37
|
.command(copy as unknown as CommandModule)
|
|
26
38
|
.command(doc as unknown as CommandModule)
|
|
39
|
+
.command(selfUpdate as unknown as CommandModule)
|
|
27
40
|
.demandCommand(1)
|
|
28
41
|
.help()
|
|
29
42
|
.parseAsync();
|
|
43
|
+
process.exit(0);
|
|
30
44
|
} catch (err) {
|
|
31
45
|
const message = err instanceof Error ? err.message : String(err);
|
|
32
46
|
process.stderr.write(`${message}\n`);
|
|
33
|
-
process.
|
|
47
|
+
process.exit(1);
|
|
34
48
|
}
|
|
@@ -94,7 +94,7 @@ export interface DocCommandArgs extends GenerateCommandArgs {
|
|
|
94
94
|
/**
|
|
95
95
|
* Available scaffolding template identifiers for the create command.
|
|
96
96
|
*/
|
|
97
|
-
export type CreateTemplateId = "types-locally" | "types-npm" | "types-workspace";
|
|
97
|
+
export type CreateTemplateId = "types-locally" | "types-npm" | "types-workspace" | "types-gjsify";
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Arguments for the create command
|
|
@@ -145,3 +145,10 @@ export interface AnalyzeCommandArgs {
|
|
|
145
145
|
/** Switch on/off the verbose mode */
|
|
146
146
|
verbose?: boolean;
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
export interface SelfUpdateCommandArgs {
|
|
150
|
+
/** Only check for a newer version, do not install */
|
|
151
|
+
check: boolean;
|
|
152
|
+
/** Force reinstall even if already on the latest version */
|
|
153
|
+
force: boolean;
|
|
154
|
+
}
|