@ts-for-gir/cli 4.0.0-rc.5 → 4.0.0-rc.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/bin/ts-for-gir +107 -28
- 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 +10 -10
- package/src/commands/run-generation-command.ts +6 -1
- package/src/config/config-loader.ts +50 -3
- package/src/config/defaults.ts +2 -0
- package/src/config/options.ts +23 -0
- package/src/module-loader.ts +46 -14
- package/src/types/command-args.ts +6 -0
package/bin/ts-for-gir
CHANGED
|
@@ -7268,7 +7268,7 @@ import { dirname, join } from "node:path";
|
|
|
7268
7268
|
import { fileURLToPath } from "node:url";
|
|
7269
7269
|
function getPackageVersion() {
|
|
7270
7270
|
if (true) {
|
|
7271
|
-
return "4.0.0-rc.
|
|
7271
|
+
return "4.0.0-rc.6";
|
|
7272
7272
|
}
|
|
7273
7273
|
const currentModulePath = fileURLToPath(import.meta.url);
|
|
7274
7274
|
const currentDir = dirname(currentModulePath);
|
|
@@ -8165,7 +8165,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
8165
8165
|
var NEW_LINE_REG_EXP = /[\n\r]+/g;
|
|
8166
8166
|
function getPackageVersion2() {
|
|
8167
8167
|
if (true) {
|
|
8168
|
-
return "4.0.0-rc.
|
|
8168
|
+
return "4.0.0-rc.6";
|
|
8169
8169
|
}
|
|
8170
8170
|
const currentModulePath = fileURLToPath2(import.meta.url);
|
|
8171
8171
|
const currentDir = dirname2(currentModulePath);
|
|
@@ -12726,6 +12726,12 @@ var DependencyManager = class _DependencyManager {
|
|
|
12726
12726
|
};
|
|
12727
12727
|
}
|
|
12728
12728
|
createImportPath(packageName, namespace, version) {
|
|
12729
|
+
if (this.config.externalDeps) {
|
|
12730
|
+
const override3 = this.config.externalPackages?.[namespace];
|
|
12731
|
+
if (override3) return override3;
|
|
12732
|
+
const importName2 = transformImportName(packageName);
|
|
12733
|
+
return `${this.config.npmScope}/${importName2}`;
|
|
12734
|
+
}
|
|
12729
12735
|
if (!this.config.package) {
|
|
12730
12736
|
return `gi://${namespace}?version=${version}`;
|
|
12731
12737
|
}
|
|
@@ -20380,19 +20386,21 @@ var NSRegistry = class {
|
|
|
20380
20386
|
);
|
|
20381
20387
|
}
|
|
20382
20388
|
transform(options2) {
|
|
20383
|
-
const GLib = this.
|
|
20384
|
-
const Gio = this.
|
|
20385
|
-
const GObject = this.
|
|
20386
|
-
Gio.package_version = [...GLib.package_version];
|
|
20387
|
-
GObject.package_version = [...GLib.package_version];
|
|
20389
|
+
const GLib = this.namespace("GLib", "2.0");
|
|
20390
|
+
const Gio = this.namespace("Gio", "2.0");
|
|
20391
|
+
const GObject = this.namespace("GObject", "2.0");
|
|
20392
|
+
if (GLib && Gio) Gio.package_version = [...GLib.package_version];
|
|
20393
|
+
if (GLib && GObject) GObject.package_version = [...GLib.package_version];
|
|
20388
20394
|
const interfaceVisitor = new InterfaceVisitor();
|
|
20389
20395
|
this.registerTransformation(interfaceVisitor);
|
|
20390
20396
|
const classVisitor = new ClassVisitor();
|
|
20391
20397
|
this.registerTransformation(classVisitor);
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
20398
|
+
if (GLib && Gio) {
|
|
20399
|
+
console.log("Adding generics...");
|
|
20400
|
+
generify(this, options2.inferGenerics);
|
|
20401
|
+
console.log("Injecting types...");
|
|
20402
|
+
inject(this);
|
|
20403
|
+
}
|
|
20396
20404
|
}
|
|
20397
20405
|
defaultVersionOf(name) {
|
|
20398
20406
|
if (name === "GLib" || name === "Gio" || name === "GObject") {
|
|
@@ -20989,6 +20997,8 @@ var defaults = {
|
|
|
20989
20997
|
package: false,
|
|
20990
20998
|
reporter: false,
|
|
20991
20999
|
reporterOutput: "ts-for-gir-report.json",
|
|
21000
|
+
externalDeps: false,
|
|
21001
|
+
allowMissingDeps: false,
|
|
20992
21002
|
combined: true,
|
|
20993
21003
|
/** Default theme for `ts-for-gir doc` (HTML documentation). */
|
|
20994
21004
|
theme: "gi-docgen"
|
|
@@ -21178,6 +21188,23 @@ var options = {
|
|
|
21178
21188
|
description: "Output file path for the reporter (default: ts-for-gir-report.json)",
|
|
21179
21189
|
default: defaults.reporterOutput,
|
|
21180
21190
|
normalize: true
|
|
21191
|
+
},
|
|
21192
|
+
externalDeps: {
|
|
21193
|
+
type: "boolean",
|
|
21194
|
+
description: "Emit imports from installed @girs/* npm packages instead of regenerating dep types. Implies single-file ambient .d.ts output. Designed for project-local Vala/C bridges. Strict by default \u2014 missing transitive dep GIRs abort the run; pass --allow-missing-deps to override.",
|
|
21195
|
+
default: defaults.externalDeps,
|
|
21196
|
+
normalize: true
|
|
21197
|
+
},
|
|
21198
|
+
allowMissingDeps: {
|
|
21199
|
+
type: "boolean",
|
|
21200
|
+
description: "In --external-deps mode, allow generation to proceed when app-specific transitive dep GIRs are missing (e.g. CI without libsoup3-devel). Note: GLib/Gio/GObject GIRs are still architecturally required for class-hierarchy resolution. Default strict behavior prevents silent type-quality drift between environments.",
|
|
21201
|
+
default: defaults.allowMissingDeps,
|
|
21202
|
+
normalize: true
|
|
21203
|
+
},
|
|
21204
|
+
externalPackage: {
|
|
21205
|
+
type: "string",
|
|
21206
|
+
description: "Override the default namespace\u2192npm package mapping for --external-deps mode. Repeatable. Format: 'Namespace=@scope/pkg'. Example: --external-package Soup=@girs/soup-3.0",
|
|
21207
|
+
array: true
|
|
21181
21208
|
}
|
|
21182
21209
|
};
|
|
21183
21210
|
var generateOptions = {
|
|
@@ -21201,7 +21228,10 @@ var generateOptions = {
|
|
|
21201
21228
|
noAdvancedVariants: options.noAdvancedVariants,
|
|
21202
21229
|
package: options.package,
|
|
21203
21230
|
reporter: options.reporter,
|
|
21204
|
-
reporterOutput: options.reporterOutput
|
|
21231
|
+
reporterOutput: options.reporterOutput,
|
|
21232
|
+
externalDeps: options.externalDeps,
|
|
21233
|
+
allowMissingDeps: options.allowMissingDeps,
|
|
21234
|
+
externalPackage: options.externalPackage
|
|
21205
21235
|
};
|
|
21206
21236
|
var listOptions = {
|
|
21207
21237
|
modules: options.modules,
|
|
@@ -21393,6 +21423,18 @@ function getOptionsGeneration(config) {
|
|
|
21393
21423
|
};
|
|
21394
21424
|
return generateConfig;
|
|
21395
21425
|
}
|
|
21426
|
+
function parseExternalPackagePairs(pairs) {
|
|
21427
|
+
if (!pairs || pairs.length === 0) return void 0;
|
|
21428
|
+
const map2 = {};
|
|
21429
|
+
for (const pair of pairs) {
|
|
21430
|
+
const eq = pair.indexOf("=");
|
|
21431
|
+
if (eq < 1) continue;
|
|
21432
|
+
const ns = pair.slice(0, eq).trim();
|
|
21433
|
+
const pkg = pair.slice(eq + 1).trim();
|
|
21434
|
+
if (ns && pkg) map2[ns] = pkg;
|
|
21435
|
+
}
|
|
21436
|
+
return Object.keys(map2).length > 0 ? map2 : void 0;
|
|
21437
|
+
}
|
|
21396
21438
|
function validate2(config) {
|
|
21397
21439
|
return config;
|
|
21398
21440
|
}
|
|
@@ -21410,9 +21452,16 @@ function mergeConfigValue(userConfig, configFileData, key, optionDefault, valida
|
|
|
21410
21452
|
async function load(cliOptions) {
|
|
21411
21453
|
const configFile = await loadConfigFile(cliOptions.configName);
|
|
21412
21454
|
const configFileData = configFile?.config || {};
|
|
21455
|
+
const externalPackagesFromCli = parseExternalPackagePairs(
|
|
21456
|
+
cliOptions.externalPackage
|
|
21457
|
+
);
|
|
21458
|
+
const { externalPackage: _externalPackage, ...cliOptionsClean } = cliOptions;
|
|
21413
21459
|
const userConfig = {
|
|
21414
|
-
...
|
|
21460
|
+
...cliOptionsClean
|
|
21415
21461
|
};
|
|
21462
|
+
if (externalPackagesFromCli) {
|
|
21463
|
+
userConfig.externalPackages = externalPackagesFromCli;
|
|
21464
|
+
}
|
|
21416
21465
|
if (configFileData) {
|
|
21417
21466
|
const booleanKeys = [
|
|
21418
21467
|
["verbose", options.verbose.default],
|
|
@@ -21427,6 +21476,8 @@ async function load(cliOptions) {
|
|
|
21427
21476
|
["noAdvancedVariants", options.noAdvancedVariants.default],
|
|
21428
21477
|
["package", options.package.default],
|
|
21429
21478
|
["reporter", options.reporter.default],
|
|
21479
|
+
["externalDeps", options.externalDeps.default],
|
|
21480
|
+
["allowMissingDeps", options.allowMissingDeps.default],
|
|
21430
21481
|
["combined", docOptions.combined.default],
|
|
21431
21482
|
["merge", docOptions.merge.default]
|
|
21432
21483
|
];
|
|
@@ -21446,13 +21497,19 @@ async function load(cliOptions) {
|
|
|
21446
21497
|
mergeConfigValue(userConfig, configFileData, key, defaultVal);
|
|
21447
21498
|
}
|
|
21448
21499
|
const arrayKeys = [
|
|
21449
|
-
["girDirectories", options.girDirectories.default],
|
|
21450
21500
|
["ignore", options.ignore.default],
|
|
21451
21501
|
["modules", options.modules.default]
|
|
21452
21502
|
];
|
|
21453
21503
|
for (const [key, defaultVal] of arrayKeys) {
|
|
21454
21504
|
mergeConfigValue(userConfig, configFileData, key, defaultVal);
|
|
21455
21505
|
}
|
|
21506
|
+
if (configFileData.girDirectories?.length) {
|
|
21507
|
+
const current = userConfig.girDirectories;
|
|
21508
|
+
const toAdd = configFileData.girDirectories.filter((d) => !current.includes(d));
|
|
21509
|
+
if (toAdd.length > 0) {
|
|
21510
|
+
userConfig.girDirectories = [...toAdd, ...current];
|
|
21511
|
+
}
|
|
21512
|
+
}
|
|
21456
21513
|
if (userConfig.root === options.root.default && (configFileData.root || configFile?.filepath)) {
|
|
21457
21514
|
userConfig.root = configFileData.root || (configFile?.filepath ? dirname5(configFile.filepath) : options.root.default);
|
|
21458
21515
|
}
|
|
@@ -21460,6 +21517,9 @@ async function load(cliOptions) {
|
|
|
21460
21517
|
if (isDefaultOutdir && configFileData.outdir) {
|
|
21461
21518
|
userConfig.outdir = userConfig.print ? null : configFileData.outdir;
|
|
21462
21519
|
}
|
|
21520
|
+
if (!externalPackagesFromCli && configFileData.externalPackages) {
|
|
21521
|
+
userConfig.externalPackages = configFileData.externalPackages;
|
|
21522
|
+
}
|
|
21463
21523
|
}
|
|
21464
21524
|
const resolveToRoot = (path) => path.startsWith("/") ? path : resolve4(userConfig.root, path);
|
|
21465
21525
|
if (userConfig.outdir) {
|
|
@@ -22136,7 +22196,9 @@ var ModuleLoader = class {
|
|
|
22136
22196
|
const girModule = await this.loadAndCreateGirModule(dependency);
|
|
22137
22197
|
if (!girModule) {
|
|
22138
22198
|
if (!failedGirModules.has(dependency.packageName)) {
|
|
22139
|
-
this.
|
|
22199
|
+
if (!this.config.externalDeps) {
|
|
22200
|
+
this.log.warn(WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE(dependency.packageName));
|
|
22201
|
+
}
|
|
22140
22202
|
failedGirModules.add(dependency.packageName);
|
|
22141
22203
|
}
|
|
22142
22204
|
} else if (girModule?.packageName) {
|
|
@@ -22190,18 +22252,33 @@ var ModuleLoader = class {
|
|
|
22190
22252
|
const GObject = await this.dependencyManager.get("GObject", "2.0");
|
|
22191
22253
|
const Cairo = await this.dependencyManager.get("cairo", "1.0");
|
|
22192
22254
|
const dependencies = await this.fileFinder.girFilePathToDependencies(girFiles);
|
|
22255
|
+
const { loaded: forceLoaded, failed: forceFailed } = await this.loadGirModules(
|
|
22256
|
+
[GLib, Gio, GObject, Cairo],
|
|
22257
|
+
ignore,
|
|
22258
|
+
[],
|
|
22259
|
+
0 /* DEPENDENCE */
|
|
22260
|
+
);
|
|
22193
22261
|
const { loaded, failed } = await this.loadGirModules(
|
|
22194
|
-
|
|
22195
|
-
GLib
|
|
22196
|
-
|
|
22197
|
-
|
|
22198
|
-
|
|
22199
|
-
|
|
22200
|
-
|
|
22201
|
-
)
|
|
22202
|
-
],
|
|
22203
|
-
ignore
|
|
22262
|
+
dependencies.filter(
|
|
22263
|
+
(dep) => dep.namespace !== "GLib" && dep.namespace !== "Gio" && dep.namespace !== "GObject" && dep.namespace !== "cairo"
|
|
22264
|
+
),
|
|
22265
|
+
ignore,
|
|
22266
|
+
forceLoaded,
|
|
22267
|
+
1 /* BY_HAND */,
|
|
22268
|
+
forceFailed
|
|
22204
22269
|
);
|
|
22270
|
+
if (this.config.externalDeps && !this.config.allowMissingDeps && failed.size > 0) {
|
|
22271
|
+
const exempt = /* @__PURE__ */ new Set(["GLib-2.0", "Gio-2.0", "GObject-2.0", "cairo-1.0"]);
|
|
22272
|
+
const critical = Array.from(failed).filter((pkg) => !exempt.has(pkg));
|
|
22273
|
+
if (critical.length > 0) {
|
|
22274
|
+
throw new Error(
|
|
22275
|
+
`Missing GIR files for transitive dependencies in --external-deps mode:
|
|
22276
|
+
` + critical.map((pkg) => ` - ${pkg}`).join("\n") + `
|
|
22277
|
+
|
|
22278
|
+
Install the corresponding -devel packages, add their directories to --girDirectories, or pass --allow-missing-deps to generate anyway (warning: degraded type quality).`
|
|
22279
|
+
);
|
|
22280
|
+
}
|
|
22281
|
+
}
|
|
22205
22282
|
let keep = [];
|
|
22206
22283
|
if (doNotAskForVersionOnConflict) {
|
|
22207
22284
|
keep = loaded;
|
|
@@ -24548,6 +24625,7 @@ var TypeDefinitionGenerator = class _TypeDefinitionGenerator {
|
|
|
24548
24625
|
}
|
|
24549
24626
|
async exportGjs() {
|
|
24550
24627
|
const { config, dependencyManager } = this;
|
|
24628
|
+
if (config.externalDeps) return;
|
|
24551
24629
|
if (!config.outdir) return;
|
|
24552
24630
|
const gjs2 = dependencyManager.getGjs();
|
|
24553
24631
|
const templateProcessor = new TemplateProcessor(
|
|
@@ -24638,7 +24716,7 @@ ${domContent.append}`, config.outdir, "dom.d.ts");
|
|
|
24638
24716
|
}
|
|
24639
24717
|
async finish(girModules) {
|
|
24640
24718
|
await this.exportGjs();
|
|
24641
|
-
if (!this.config.package) {
|
|
24719
|
+
if (!this.config.package && !this.config.externalDeps) {
|
|
24642
24720
|
await this.exportAllModules(girModules);
|
|
24643
24721
|
}
|
|
24644
24722
|
}
|
|
@@ -27498,7 +27576,7 @@ import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
|
27498
27576
|
import { i18n, JSX as JSX8, ReflectionKind as ReflectionKind4 } from "typedoc";
|
|
27499
27577
|
function getTsForGirVersion() {
|
|
27500
27578
|
if (true) {
|
|
27501
|
-
return "4.0.0-rc.
|
|
27579
|
+
return "4.0.0-rc.6";
|
|
27502
27580
|
}
|
|
27503
27581
|
const __dirname3 = dirname8(fileURLToPath5(import.meta.url));
|
|
27504
27582
|
return JSON.parse(readFileSync7(join14(__dirname3, "..", "..", "package.json"), "utf8")).version;
|
|
@@ -28204,7 +28282,8 @@ async function runGenerationCommand(args, options2) {
|
|
|
28204
28282
|
}
|
|
28205
28283
|
tsForGir = new GenerationHandler(generateConfig, options2.generatorType, registry);
|
|
28206
28284
|
moduleLoader.parse(keep);
|
|
28207
|
-
const
|
|
28285
|
+
const toGenerate = generateConfig.externalDeps ? keep.filter((m) => m.resolvedBy === 1 /* BY_HAND */) : keep;
|
|
28286
|
+
const girModules = Array.from(toGenerate).map((girModuleResolvedBy) => girModuleResolvedBy.module);
|
|
28208
28287
|
await tsForGir.start(girModules);
|
|
28209
28288
|
} catch (error) {
|
|
28210
28289
|
if (generateConfig.reporter && tsForGir) {
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"typescript": "^6.0.2"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@girs/adw-1": "^1.10.0-4.0.0-rc.
|
|
18
|
-
"@girs/gio-2.0": "^2.88.0-4.0.0-rc.
|
|
19
|
-
"@girs/gjs": "^4.0.0-rc.
|
|
20
|
-
"@girs/glib-2.0": "^2.88.0-4.0.0-rc.
|
|
21
|
-
"@girs/gtk-4.0": "^4.23.0-4.0.0-rc.
|
|
17
|
+
"@girs/adw-1": "^1.10.0-4.0.0-rc.5",
|
|
18
|
+
"@girs/gio-2.0": "^2.88.0-4.0.0-rc.5",
|
|
19
|
+
"@girs/gjs": "^4.0.0-rc.5",
|
|
20
|
+
"@girs/glib-2.0": "^2.88.0-4.0.0-rc.5",
|
|
21
|
+
"@girs/gtk-4.0": "^4.23.0-4.0.0-rc.5"
|
|
22
22
|
}
|
|
23
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-for-gir/cli",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.6",
|
|
4
4
|
"description": "TypeScript type definition generator for GObject introspection GIR files",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -55,14 +55,14 @@
|
|
|
55
55
|
".": "./src/index.ts"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@gi.ts/parser": "^4.0.0-rc.
|
|
59
|
-
"@ts-for-gir/generator-base": "^4.0.0-rc.
|
|
60
|
-
"@ts-for-gir/generator-html-doc": "^4.0.0-rc.
|
|
61
|
-
"@ts-for-gir/generator-json": "^4.0.0-rc.
|
|
62
|
-
"@ts-for-gir/generator-typescript": "^4.0.0-rc.
|
|
63
|
-
"@ts-for-gir/lib": "^4.0.0-rc.
|
|
64
|
-
"@ts-for-gir/reporter": "^4.0.0-rc.
|
|
65
|
-
"@ts-for-gir/tsconfig": "^4.0.0-rc.
|
|
58
|
+
"@gi.ts/parser": "^4.0.0-rc.6",
|
|
59
|
+
"@ts-for-gir/generator-base": "^4.0.0-rc.6",
|
|
60
|
+
"@ts-for-gir/generator-html-doc": "^4.0.0-rc.6",
|
|
61
|
+
"@ts-for-gir/generator-json": "^4.0.0-rc.6",
|
|
62
|
+
"@ts-for-gir/generator-typescript": "^4.0.0-rc.6",
|
|
63
|
+
"@ts-for-gir/lib": "^4.0.0-rc.6",
|
|
64
|
+
"@ts-for-gir/reporter": "^4.0.0-rc.6",
|
|
65
|
+
"@ts-for-gir/tsconfig": "^4.0.0-rc.6",
|
|
66
66
|
"@types/ejs": "^3.1.5",
|
|
67
67
|
"@types/inquirer": "^9.0.9",
|
|
68
68
|
"@types/node": "^25.6.0",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
74
|
"@inquirer/prompts": "^8.4.2",
|
|
75
|
-
"@ts-for-gir/templates": "^4.0.0-rc.
|
|
75
|
+
"@ts-for-gir/templates": "^4.0.0-rc.6",
|
|
76
76
|
"colorette": "^2.0.20",
|
|
77
77
|
"cosmiconfig": "^9.0.1",
|
|
78
78
|
"ejs": "^5.0.2",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Logger,
|
|
7
7
|
NSRegistry,
|
|
8
8
|
ReporterService,
|
|
9
|
+
ResolveType,
|
|
9
10
|
} from "@ts-for-gir/lib";
|
|
10
11
|
import { getOptionsGeneration, load } from "../config.ts";
|
|
11
12
|
import { GenerationHandler } from "../generation-handler.ts";
|
|
@@ -45,7 +46,11 @@ export async function runGenerationCommand(args: ConfigFlags, options: Generatio
|
|
|
45
46
|
|
|
46
47
|
moduleLoader.parse(keep);
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
// In external-deps mode, only generate the user-requested module(s). Transitively-loaded
|
|
50
|
+
// deps stay in the registry for type resolution but must not produce their own output.
|
|
51
|
+
const toGenerate = generateConfig.externalDeps ? keep.filter((m) => m.resolvedBy === ResolveType.BY_HAND) : keep;
|
|
52
|
+
|
|
53
|
+
const girModules = Array.from(toGenerate).map((girModuleResolvedBy) => girModuleResolvedBy.module as GirModule);
|
|
49
54
|
|
|
50
55
|
await tsForGir.start(girModules);
|
|
51
56
|
} catch (error) {
|
|
@@ -59,6 +59,24 @@ export function getOptionsGeneration(config: UserConfig): OptionsGeneration {
|
|
|
59
59
|
return generateConfig;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Parse `Namespace=npm-package` strings (from repeatable `--external-package` flag) into a
|
|
64
|
+
* map. Silently drops entries that don't contain `=`. Empty input returns undefined so the
|
|
65
|
+
* field stays absent in the merged config (rather than `{}`, which would shadow rc values).
|
|
66
|
+
*/
|
|
67
|
+
function parseExternalPackagePairs(pairs: string[] | undefined): Record<string, string> | undefined {
|
|
68
|
+
if (!pairs || pairs.length === 0) return undefined;
|
|
69
|
+
const map: Record<string, string> = {};
|
|
70
|
+
for (const pair of pairs) {
|
|
71
|
+
const eq = pair.indexOf("=");
|
|
72
|
+
if (eq < 1) continue;
|
|
73
|
+
const ns = pair.slice(0, eq).trim();
|
|
74
|
+
const pkg = pair.slice(eq + 1).trim();
|
|
75
|
+
if (ns && pkg) map[ns] = pkg;
|
|
76
|
+
}
|
|
77
|
+
return Object.keys(map).length > 0 ? map : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
/**
|
|
63
81
|
* Validate the configuration
|
|
64
82
|
*/
|
|
@@ -111,9 +129,21 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
111
129
|
const configFile = await loadConfigFile(cliOptions.configName);
|
|
112
130
|
const configFileData = configFile?.config || {};
|
|
113
131
|
|
|
132
|
+
// `--external-package GLib=@girs/glib-2.0` arrives as a string[]; collapse to Record.
|
|
133
|
+
// Drop the raw array so it doesn't pollute the merged UserConfig surface.
|
|
134
|
+
const externalPackagesFromCli = parseExternalPackagePairs(
|
|
135
|
+
(cliOptions as { externalPackage?: string[] }).externalPackage,
|
|
136
|
+
);
|
|
137
|
+
const { externalPackage: _externalPackage, ...cliOptionsClean } = cliOptions as ConfigFlags & {
|
|
138
|
+
externalPackage?: string[];
|
|
139
|
+
};
|
|
140
|
+
|
|
114
141
|
const userConfig: UserConfig = {
|
|
115
|
-
...
|
|
142
|
+
...cliOptionsClean,
|
|
116
143
|
};
|
|
144
|
+
if (externalPackagesFromCli) {
|
|
145
|
+
userConfig.externalPackages = externalPackagesFromCli;
|
|
146
|
+
}
|
|
117
147
|
|
|
118
148
|
if (configFileData) {
|
|
119
149
|
// Boolean options — config file overrides CLI defaults
|
|
@@ -130,6 +160,8 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
130
160
|
["noAdvancedVariants", options.noAdvancedVariants.default],
|
|
131
161
|
["package", options.package.default],
|
|
132
162
|
["reporter", options.reporter.default],
|
|
163
|
+
["externalDeps", options.externalDeps.default],
|
|
164
|
+
["allowMissingDeps", options.allowMissingDeps.default],
|
|
133
165
|
["combined", docOptions.combined.default],
|
|
134
166
|
["merge", docOptions.merge.default],
|
|
135
167
|
];
|
|
@@ -153,7 +185,6 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
153
185
|
|
|
154
186
|
// Array options — config file overrides CLI defaults
|
|
155
187
|
const arrayKeys: Array<[keyof UserConfig, unknown]> = [
|
|
156
|
-
["girDirectories", options.girDirectories.default],
|
|
157
188
|
["ignore", options.ignore.default],
|
|
158
189
|
["modules", options.modules.default],
|
|
159
190
|
];
|
|
@@ -161,6 +192,18 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
161
192
|
mergeConfigValue(userConfig, configFileData, key, defaultVal);
|
|
162
193
|
}
|
|
163
194
|
|
|
195
|
+
// girDirectories: rc-file entries are prepended to the current dirs (CLI-provided or
|
|
196
|
+
// system defaults) rather than replacing them. This lets projects add local GIR dirs
|
|
197
|
+
// (e.g. a Vala build output) without having to enumerate all system paths in the rc.
|
|
198
|
+
// To use ONLY the specified dirs (no system fallback), pass --girDirectories on the CLI.
|
|
199
|
+
if (configFileData.girDirectories?.length) {
|
|
200
|
+
const current = userConfig.girDirectories as string[];
|
|
201
|
+
const toAdd = (configFileData.girDirectories as string[]).filter((d) => !current.includes(d));
|
|
202
|
+
if (toAdd.length > 0) {
|
|
203
|
+
userConfig.girDirectories = [...toAdd, ...current];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
164
207
|
// Special handling for root
|
|
165
208
|
if (userConfig.root === options.root.default && (configFileData.root || configFile?.filepath)) {
|
|
166
209
|
userConfig.root =
|
|
@@ -172,6 +215,11 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
172
215
|
if (isDefaultOutdir && configFileData.outdir) {
|
|
173
216
|
userConfig.outdir = userConfig.print ? null : configFileData.outdir;
|
|
174
217
|
}
|
|
218
|
+
|
|
219
|
+
// externalPackages is a Record<string, string> in rc files; CLI overrides take precedence.
|
|
220
|
+
if (!externalPackagesFromCli && configFileData.externalPackages) {
|
|
221
|
+
userConfig.externalPackages = configFileData.externalPackages;
|
|
222
|
+
}
|
|
175
223
|
}
|
|
176
224
|
|
|
177
225
|
// Make paths absolute relative to root
|
|
@@ -186,6 +234,5 @@ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
|
|
|
186
234
|
if (userConfig.girDirectories) {
|
|
187
235
|
userConfig.girDirectories = userConfig.girDirectories.map(resolveToRoot);
|
|
188
236
|
}
|
|
189
|
-
|
|
190
237
|
return validate(userConfig);
|
|
191
238
|
}
|
package/src/config/defaults.ts
CHANGED
|
@@ -31,6 +31,8 @@ export const defaults = {
|
|
|
31
31
|
package: false,
|
|
32
32
|
reporter: false,
|
|
33
33
|
reporterOutput: "ts-for-gir-report.json",
|
|
34
|
+
externalDeps: false,
|
|
35
|
+
allowMissingDeps: false,
|
|
34
36
|
combined: true,
|
|
35
37
|
/** Default theme for `ts-for-gir doc` (HTML documentation). */
|
|
36
38
|
theme: "gi-docgen",
|
package/src/config/options.ts
CHANGED
|
@@ -146,6 +146,26 @@ export const options: { [name: string]: Options } = {
|
|
|
146
146
|
default: defaults.reporterOutput,
|
|
147
147
|
normalize: true,
|
|
148
148
|
},
|
|
149
|
+
externalDeps: {
|
|
150
|
+
type: "boolean",
|
|
151
|
+
description:
|
|
152
|
+
"Emit imports from installed @girs/* npm packages instead of regenerating dep types. Implies single-file ambient .d.ts output. Designed for project-local Vala/C bridges. Strict by default — missing transitive dep GIRs abort the run; pass --allow-missing-deps to override.",
|
|
153
|
+
default: defaults.externalDeps,
|
|
154
|
+
normalize: true,
|
|
155
|
+
},
|
|
156
|
+
allowMissingDeps: {
|
|
157
|
+
type: "boolean",
|
|
158
|
+
description:
|
|
159
|
+
"In --external-deps mode, allow generation to proceed when app-specific transitive dep GIRs are missing (e.g. CI without libsoup3-devel). Note: GLib/Gio/GObject GIRs are still architecturally required for class-hierarchy resolution. Default strict behavior prevents silent type-quality drift between environments.",
|
|
160
|
+
default: defaults.allowMissingDeps,
|
|
161
|
+
normalize: true,
|
|
162
|
+
},
|
|
163
|
+
externalPackage: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description:
|
|
166
|
+
"Override the default namespace→npm package mapping for --external-deps mode. Repeatable. Format: 'Namespace=@scope/pkg'. Example: --external-package Soup=@girs/soup-3.0",
|
|
167
|
+
array: true,
|
|
168
|
+
},
|
|
149
169
|
};
|
|
150
170
|
|
|
151
171
|
/**
|
|
@@ -173,6 +193,9 @@ export const generateOptions = {
|
|
|
173
193
|
package: options.package,
|
|
174
194
|
reporter: options.reporter,
|
|
175
195
|
reporterOutput: options.reporterOutput,
|
|
196
|
+
externalDeps: options.externalDeps,
|
|
197
|
+
allowMissingDeps: options.allowMissingDeps,
|
|
198
|
+
externalPackage: options.externalPackage,
|
|
176
199
|
};
|
|
177
200
|
|
|
178
201
|
export const listOptions = {
|
package/src/module-loader.ts
CHANGED
|
@@ -168,7 +168,13 @@ export class ModuleLoader {
|
|
|
168
168
|
const girModule = await this.loadAndCreateGirModule(dependency);
|
|
169
169
|
if (!girModule) {
|
|
170
170
|
if (!failedGirModules.has(dependency.packageName)) {
|
|
171
|
-
|
|
171
|
+
// In external-deps mode the strict check after loading turns missing
|
|
172
|
+
// transitive deps into a hard error; suppress the per-dep warn here so
|
|
173
|
+
// the user sees one consolidated error instead of a wall of warnings.
|
|
174
|
+
// `--allow-missing-deps` opts into the same suppression with a soft fail.
|
|
175
|
+
if (!this.config.externalDeps) {
|
|
176
|
+
this.log.warn(WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE(dependency.packageName));
|
|
177
|
+
}
|
|
172
178
|
failedGirModules.add(dependency.packageName);
|
|
173
179
|
}
|
|
174
180
|
} else if (girModule?.packageName) {
|
|
@@ -239,23 +245,49 @@ export class ModuleLoader {
|
|
|
239
245
|
|
|
240
246
|
const dependencies = await this.fileFinder.girFilePathToDependencies(girFiles);
|
|
241
247
|
|
|
248
|
+
// Load GJS force-loads as DEPENDENCE so they are never treated as user-requested
|
|
249
|
+
// modules. This ensures --external-deps mode doesn't emit output files for them.
|
|
250
|
+
const { loaded: forceLoaded, failed: forceFailed } = await this.loadGirModules(
|
|
251
|
+
[GLib, Gio, GObject, Cairo],
|
|
252
|
+
ignore,
|
|
253
|
+
[],
|
|
254
|
+
ResolveType.DEPENDENCE,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Load user-requested modules (and their transitive deps) starting from the
|
|
258
|
+
// already-loaded force-load registry so they are not double-loaded.
|
|
242
259
|
const { loaded, failed } = await this.loadGirModules(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
dep.namespace !== "GLib" &&
|
|
251
|
-
dep.namespace !== "Gio" &&
|
|
252
|
-
dep.namespace !== "GObject" &&
|
|
253
|
-
dep.namespace !== "cairo",
|
|
254
|
-
),
|
|
255
|
-
],
|
|
260
|
+
dependencies.filter(
|
|
261
|
+
(dep) =>
|
|
262
|
+
dep.namespace !== "GLib" &&
|
|
263
|
+
dep.namespace !== "Gio" &&
|
|
264
|
+
dep.namespace !== "GObject" &&
|
|
265
|
+
dep.namespace !== "cairo",
|
|
266
|
+
),
|
|
256
267
|
ignore,
|
|
268
|
+
forceLoaded,
|
|
269
|
+
ResolveType.BY_HAND,
|
|
270
|
+
forceFailed,
|
|
257
271
|
);
|
|
258
272
|
|
|
273
|
+
// External-deps mode is strict by default: any transitive dep GIR that couldn't be
|
|
274
|
+
// loaded would silently degrade type quality. The hardcoded GLib/Gio/GObject/cairo
|
|
275
|
+
// force-loads above are exempt (they exist for runtime convenience, not because the
|
|
276
|
+
// input GIR references them).
|
|
277
|
+
if (this.config.externalDeps && !this.config.allowMissingDeps && failed.size > 0) {
|
|
278
|
+
const exempt = new Set(["GLib-2.0", "Gio-2.0", "GObject-2.0", "cairo-1.0"]);
|
|
279
|
+
const critical = Array.from(failed).filter((pkg) => !exempt.has(pkg));
|
|
280
|
+
if (critical.length > 0) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`Missing GIR files for transitive dependencies in --external-deps mode:\n` +
|
|
283
|
+
critical.map((pkg) => ` - ${pkg}`).join("\n") +
|
|
284
|
+
`\n\nInstall the corresponding -devel packages, add their directories to ` +
|
|
285
|
+
`--girDirectories, or pass --allow-missing-deps to generate anyway ` +
|
|
286
|
+
`(warning: degraded type quality).`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
259
291
|
let keep: GirModuleResolvedBy[] = [];
|
|
260
292
|
if (doNotAskForVersionOnConflict) {
|
|
261
293
|
keep = loaded;
|
|
@@ -50,6 +50,12 @@ export interface GenerateCommandArgs extends BaseCommandArgs {
|
|
|
50
50
|
reporter: boolean;
|
|
51
51
|
/** Output file path for the reporter */
|
|
52
52
|
reporterOutput: string;
|
|
53
|
+
/** Emit imports from installed @girs/* npm packages instead of regenerating dep types */
|
|
54
|
+
externalDeps: boolean;
|
|
55
|
+
/** Allow externalDeps generation when transitive dep GIRs are missing (degraded type quality) */
|
|
56
|
+
allowMissingDeps: boolean;
|
|
57
|
+
/** Override default namespace→npm package mapping. Repeatable. Format: `Namespace=@girs/pkg` */
|
|
58
|
+
externalPackage?: string[];
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
/**
|