@ts-for-gir/cli 4.0.0-beta.3 → 4.0.0-beta.30

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.
Files changed (63) hide show
  1. package/README.md +184 -173
  2. package/bin/ts-for-gir +20046 -0
  3. package/bin/ts-for-gir-dev +43 -0
  4. package/package.json +38 -37
  5. package/src/commands/analyze.ts +344 -0
  6. package/src/commands/command-builder.ts +30 -0
  7. package/src/commands/copy.ts +71 -76
  8. package/src/commands/doc.ts +58 -46
  9. package/src/commands/generate.ts +97 -78
  10. package/src/commands/index.ts +6 -4
  11. package/src/commands/json.ts +104 -0
  12. package/src/commands/list.ts +81 -90
  13. package/src/config/config-loader.ts +203 -0
  14. package/src/config/config-writer.ts +52 -0
  15. package/src/config/defaults.ts +61 -0
  16. package/src/config/index.ts +8 -0
  17. package/src/config/options.ts +292 -0
  18. package/src/config.ts +3 -449
  19. package/src/formatters/typescript-formatter.ts +24 -0
  20. package/src/generation-handler.ts +122 -67
  21. package/src/index.ts +4 -4
  22. package/src/module-loader/dependency-resolver.ts +100 -0
  23. package/src/module-loader/file-finder.ts +58 -0
  24. package/src/module-loader/index.ts +8 -0
  25. package/src/module-loader/module-grouper.ts +77 -0
  26. package/src/module-loader/prompt-handler.ts +111 -0
  27. package/src/module-loader.ts +280 -578
  28. package/src/start.ts +17 -14
  29. package/src/types/command-args.ts +110 -0
  30. package/src/types/command-definition.ts +15 -0
  31. package/src/types/commands.ts +35 -0
  32. package/src/types/index.ts +15 -0
  33. package/src/types/report-types.ts +34 -0
  34. package/lib/commands/copy.d.ts +0 -12
  35. package/lib/commands/copy.js +0 -80
  36. package/lib/commands/copy.js.map +0 -1
  37. package/lib/commands/doc.d.ts +0 -12
  38. package/lib/commands/doc.js +0 -40
  39. package/lib/commands/doc.js.map +0 -1
  40. package/lib/commands/generate.d.ts +0 -12
  41. package/lib/commands/generate.js +0 -73
  42. package/lib/commands/generate.js.map +0 -1
  43. package/lib/commands/index.d.ts +0 -4
  44. package/lib/commands/index.js +0 -5
  45. package/lib/commands/index.js.map +0 -1
  46. package/lib/commands/list.d.ts +0 -12
  47. package/lib/commands/list.js +0 -81
  48. package/lib/commands/list.js.map +0 -1
  49. package/lib/config.d.ts +0 -104
  50. package/lib/config.js +0 -407
  51. package/lib/config.js.map +0 -1
  52. package/lib/generation-handler.d.ts +0 -10
  53. package/lib/generation-handler.js +0 -47
  54. package/lib/generation-handler.js.map +0 -1
  55. package/lib/index.d.ts +0 -4
  56. package/lib/index.js +0 -5
  57. package/lib/index.js.map +0 -1
  58. package/lib/module-loader.d.ts +0 -148
  59. package/lib/module-loader.js +0 -468
  60. package/lib/module-loader.js.map +0 -1
  61. package/lib/start.d.ts +0 -2
  62. package/lib/start.js +0 -16
  63. package/lib/start.js.map +0 -1
@@ -2,101 +2,92 @@
2
2
  * Everything you need for the `ts-for-gir list` command is located here
3
3
  */
4
4
 
5
- import { Argv, BuilderCallback } from 'yargs'
6
- import { ModuleLoader } from '../module-loader.js'
7
- import { Config } from '../config.js'
8
- import { Logger, ERROR_NO_MODULES_FOUND, ResolveType } from '@ts-for-gir/lib'
5
+ import type { ConfigFlags } from "@ts-for-gir/lib";
6
+ import { APP_NAME, ERROR_NO_MODULES_FOUND, Logger, NSRegistry, ResolveType } from "@ts-for-gir/lib";
7
+ import { getOptionsGeneration, listOptions, load } from "../config.ts";
8
+ import { ModuleLoader } from "../module-loader.ts";
9
+ import type { ListCommandArgs } from "../types/index.ts";
10
+ import { createBuilder } from "./command-builder.ts";
9
11
 
10
- import type { ConfigFlags } from '@ts-for-gir/lib'
12
+ const command = "list [modules..]";
11
13
 
12
- const command = 'list [modules..]'
14
+ const description = "Lists all available GIR modules";
13
15
 
14
- const description = 'Lists all available GIR modules'
16
+ const logger = new Logger(false, "ListCommand");
15
17
 
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- const builder: BuilderCallback<any, ConfigFlags> = (yargs: Argv<any>) => {
18
- const optionNames = Object.keys(Config.listOptions)
19
- for (const optionName of optionNames) {
20
- yargs = yargs.option(optionName, Config.listOptions[optionName])
21
- }
22
- return yargs.example(examples) as Argv<ConfigFlags>
23
- }
18
+ const examples: ReadonlyArray<[string, string?]> = [
19
+ [`${APP_NAME} list -g ./vala-girs/gir-1.0`, "Lists all available GIR modules in ./vala-girs/gir-1.0"],
20
+ [
21
+ `${APP_NAME} list --ignore=Gtk-3.0 xrandr-1.3`,
22
+ "Lists all available GIR modules in /usr/share/gir-1.0 but not Gtk-3.0 and xrandr-1.3",
23
+ ],
24
+ ];
24
25
 
25
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- const handler = async (args: ConfigFlags) => {
27
- const config = await Config.load(args)
28
- const generateConfig = Config.getOptionsGeneration(config)
29
- const moduleLoader = new ModuleLoader(generateConfig)
30
- const { grouped, failed } = await moduleLoader.getModules(config.modules, config.ignore)
31
- const moduleGroups = Object.values(grouped)
32
- if (Object.keys(grouped).length === 0) {
33
- return Logger.error(ERROR_NO_MODULES_FOUND(config.girDirectories))
34
- }
35
-
36
- const conflictModules = moduleGroups.filter((moduleGroup) => moduleGroup.hasConflict)
37
-
38
- const byHandModules = moduleGroups.filter(
39
- (moduleGroup) => moduleGroup.modules[0].resolvedBy === ResolveType.BY_HAND,
40
- )
41
-
42
- const depModules = moduleGroups.filter(
43
- (moduleGroup) => moduleGroup.modules[0].resolvedBy === ResolveType.DEPENDENCE,
44
- )
45
-
46
- Logger.info('\nSearch for gir files in:')
47
- for (const dir of config.girDirectories) {
48
- Logger.white(`- ${dir}`)
49
- }
50
-
51
- Logger.info('\nSelected Modules:')
52
- for (const moduleGroup of byHandModules) {
53
- for (const depModule of moduleGroup.modules) {
54
- Logger.white(`- ${depModule.packageName}`)
55
- Logger.gray(` - ${depModule.path}`)
56
- }
57
- }
58
-
59
- if (depModules.length > 0) {
60
- Logger.yellow('\nDependencies:')
61
- for (const moduleGroup of depModules) {
62
- for (const depModule of moduleGroup.modules) {
63
- Logger.white(`- ${depModule.packageName}`)
64
- Logger.gray(`- ${depModule.path}`)
65
- }
66
- }
67
- }
68
-
69
- if (conflictModules.length > 0) {
70
- Logger.danger('\nConflicts:')
71
- for (const moduleGroup of conflictModules) {
72
- Logger.white(`- ${moduleGroup.namespace}`)
73
- for (const conflictModule of moduleGroup.modules) {
74
- Logger.white(` - ${conflictModule.packageName}`)
75
- Logger.gray(` - ${conflictModule.path}`)
76
- }
77
- }
78
- }
79
-
80
- if (failed.length > 0) {
81
- Logger.danger('\nDependencies not found:')
82
- for (const fail of failed) {
83
- Logger.white(`- ${fail}`)
84
- }
85
- }
86
- }
26
+ const builder = createBuilder<ListCommandArgs>(listOptions, examples);
87
27
 
88
- const examples: ReadonlyArray<[string, string?]> = [
89
- [`${Config.appName} list -g ./vala-girs/gir-1.0`, `Lists all available GIR modules in ./vala-girs/gir-1.0`],
90
- [
91
- `${Config.appName} list --ignore=Gtk-3.0 xrandr-1.3`,
92
- 'Lists all available GIR modules in /usr/share/gir-1.0 but not Gtk-3.0 and xrandr-1.3',
93
- ],
94
- ]
28
+ const handler = async (args: ConfigFlags) => {
29
+ const config = await load(args);
30
+ const generateConfig = getOptionsGeneration(config);
31
+ const registry = new NSRegistry(); // TODO: Use singleton
32
+ const moduleLoader = new ModuleLoader(generateConfig, registry);
33
+ const { grouped, failed } = await moduleLoader.getModules(config.modules, config.ignore);
34
+ const moduleGroups = Object.values(grouped);
35
+ if (Object.keys(grouped).length === 0) {
36
+ return logger.error(ERROR_NO_MODULES_FOUND(config.girDirectories));
37
+ }
38
+
39
+ const conflictModules = moduleGroups.filter((moduleGroup) => moduleGroup.hasConflict);
40
+
41
+ const byHandModules = moduleGroups.filter((moduleGroup) => moduleGroup.modules[0].resolvedBy === ResolveType.BY_HAND);
42
+
43
+ const depModules = moduleGroups.filter((moduleGroup) => moduleGroup.modules[0].resolvedBy === ResolveType.DEPENDENCE);
44
+
45
+ logger.info("\nSearch for gir files in:");
46
+ for (const dir of config.girDirectories) {
47
+ logger.white(`- ${dir}`);
48
+ }
49
+
50
+ logger.info("\nSelected Modules:");
51
+ for (const moduleGroup of byHandModules) {
52
+ for (const depModule of moduleGroup.modules) {
53
+ logger.white(`- ${depModule.packageName}`);
54
+ logger.gray(` - ${depModule.path}`);
55
+ }
56
+ }
57
+
58
+ if (depModules.length > 0) {
59
+ logger.yellow("\nDependencies:");
60
+ for (const moduleGroup of depModules) {
61
+ for (const depModule of moduleGroup.modules) {
62
+ logger.white(`- ${depModule.packageName}`);
63
+ logger.gray(`- ${depModule.path}`);
64
+ }
65
+ }
66
+ }
67
+
68
+ if (conflictModules.length > 0) {
69
+ logger.danger("\nConflicts:");
70
+ for (const moduleGroup of conflictModules) {
71
+ logger.white(`- ${moduleGroup.namespace}`);
72
+ for (const conflictModule of moduleGroup.modules) {
73
+ logger.white(` - ${conflictModule.packageName}`);
74
+ logger.gray(` - ${conflictModule.path}`);
75
+ }
76
+ }
77
+ }
78
+
79
+ if (failed.length > 0) {
80
+ logger.danger("\nDependencies not found:");
81
+ for (const fail of failed) {
82
+ logger.white(`- ${fail}`);
83
+ }
84
+ }
85
+ };
95
86
 
96
87
  export const list = {
97
- command,
98
- description,
99
- builder,
100
- handler,
101
- examples,
102
- }
88
+ command,
89
+ description,
90
+ builder,
91
+ handler,
92
+ examples,
93
+ };
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Config loader functionality for ts-for-gir
3
+ */
4
+
5
+ import { dirname, resolve } from "node:path";
6
+ import type { ConfigFlags, OptionsGeneration, UserConfig, UserConfigLoadResult } from "@ts-for-gir/lib";
7
+ import { APP_NAME, isEqual } from "@ts-for-gir/lib";
8
+ import { type Options as ConfigSearchOptions, cosmiconfig } from "cosmiconfig";
9
+ import { setConfigFilePath } from "./config-writer.ts";
10
+ import { options } from "./options.ts";
11
+
12
+ /**
13
+ * The user can create a `.ts-for-girrc` file for his default configs,
14
+ * this method load this config file an returns the user configuration
15
+ * @param configName If the user uses a custom config file name
16
+ */
17
+ export async function loadConfigFile(configName?: string): Promise<UserConfigLoadResult | null> {
18
+ const configSearchOptions: Partial<ConfigSearchOptions> = {
19
+ loaders: {
20
+ // ESM loader
21
+ ".js": async (filepath) => {
22
+ const file = await import(filepath);
23
+
24
+ // Files with `exports.default = { ... }`
25
+ if (file?.default?.default) {
26
+ return file.default.default as Partial<UserConfig>;
27
+ }
28
+ // Files with `export default { ... }`
29
+ if (file?.default) {
30
+ return file.default as Partial<UserConfig>;
31
+ }
32
+ // Files with `export { ... }`
33
+ return file as Partial<UserConfig>;
34
+ },
35
+ },
36
+ };
37
+
38
+ if (configName) {
39
+ configSearchOptions.searchPlaces = [configName];
40
+ }
41
+
42
+ const configFile: UserConfigLoadResult | null = await cosmiconfig(APP_NAME, configSearchOptions).search();
43
+
44
+ if (configFile?.filepath) {
45
+ setConfigFilePath(configFile.filepath);
46
+ }
47
+
48
+ return configFile;
49
+ }
50
+
51
+ /**
52
+ * Convert UserConfig to OptionsGeneration
53
+ */
54
+ export function getOptionsGeneration(config: UserConfig): OptionsGeneration {
55
+ const generateConfig: OptionsGeneration = {
56
+ ...config,
57
+ };
58
+ return generateConfig;
59
+ }
60
+
61
+ /**
62
+ * Validate the configuration
63
+ */
64
+ export function validate(config: UserConfig): UserConfig {
65
+ return config;
66
+ }
67
+
68
+ /**
69
+ * Merge a single config value from file config to user config
70
+ * @param userConfig The user config object to update
71
+ * @param configFileData The config file data to merge from
72
+ * @param key The config key to merge
73
+ * @param optionDefault The default value from options
74
+ * @param validator Optional validation function
75
+ */
76
+ function mergeConfigValue<K extends keyof UserConfig>(
77
+ userConfig: UserConfig,
78
+ configFileData: Partial<UserConfig>,
79
+ key: K,
80
+ optionDefault: unknown,
81
+ validator?: (value: unknown) => boolean,
82
+ ): void {
83
+ const fileValue = configFileData[key];
84
+ const userValue = userConfig[key];
85
+
86
+ // Skip if no file value
87
+ if (fileValue === undefined) return;
88
+
89
+ // Apply validator if provided
90
+ if (validator && !validator(fileValue)) return;
91
+
92
+ // Check if user value is default
93
+ const isDefault =
94
+ userValue === optionDefault ||
95
+ (Array.isArray(userValue) && Array.isArray(optionDefault) && isEqual(userValue, optionDefault));
96
+
97
+ if (isDefault) {
98
+ (userConfig[key] as UserConfig[K]) = fileValue as UserConfig[K];
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Loads the values of the config file and concatenate them with passed cli flags / arguments.
104
+ * The values from config file are preferred if the cli flag value is the default (and so not set / overwritten)
105
+ * @param cliOptions CLI options passed by the user
106
+ */
107
+ export async function load(cliOptions: ConfigFlags): Promise<UserConfig> {
108
+ const configFile = await loadConfigFile(cliOptions.configName);
109
+ const configFileData = configFile?.config || {};
110
+
111
+ const userConfig: UserConfig = {
112
+ ...cliOptions,
113
+ };
114
+
115
+ if (configFileData) {
116
+ // Boolean options
117
+ mergeConfigValue(userConfig, configFileData, "verbose", options.verbose.default, (v) => typeof v === "boolean");
118
+ mergeConfigValue(
119
+ userConfig,
120
+ configFileData,
121
+ "ignoreVersionConflicts",
122
+ options.ignoreVersionConflicts.default,
123
+ (v) => typeof v === "boolean",
124
+ );
125
+ mergeConfigValue(userConfig, configFileData, "print", options.print.default, (v) => typeof v === "boolean");
126
+ mergeConfigValue(
127
+ userConfig,
128
+ configFileData,
129
+ "noNamespace",
130
+ options.noNamespace.default,
131
+ (v) => typeof v === "boolean",
132
+ );
133
+ mergeConfigValue(
134
+ userConfig,
135
+ configFileData,
136
+ "noComments",
137
+ options.noComments.default,
138
+ (v) => typeof v === "boolean",
139
+ );
140
+ mergeConfigValue(userConfig, configFileData, "promisify", options.promisify.default, (v) => typeof v === "boolean");
141
+ mergeConfigValue(userConfig, configFileData, "workspace", options.workspace.default, (v) => typeof v === "boolean");
142
+ mergeConfigValue(
143
+ userConfig,
144
+ configFileData,
145
+ "onlyVersionPrefix",
146
+ options.onlyVersionPrefix.default,
147
+ (v) => typeof v === "boolean",
148
+ );
149
+ mergeConfigValue(
150
+ userConfig,
151
+ configFileData,
152
+ "noPrettyPrint",
153
+ options.noPrettyPrint.default,
154
+ (v) => typeof v === "boolean",
155
+ );
156
+ mergeConfigValue(
157
+ userConfig,
158
+ configFileData,
159
+ "noAdvancedVariants",
160
+ options.noAdvancedVariants.default,
161
+ (v) => typeof v === "boolean",
162
+ );
163
+ mergeConfigValue(userConfig, configFileData, "package", options.package.default, (v) => typeof v === "boolean");
164
+ mergeConfigValue(userConfig, configFileData, "reporter", options.reporter.default, (v) => typeof v === "boolean");
165
+
166
+ // String options
167
+ mergeConfigValue(userConfig, configFileData, "npmScope", options.npmScope.default);
168
+ mergeConfigValue(userConfig, configFileData, "reporterOutput", options.reporterOutput.default);
169
+
170
+ // Array options
171
+ mergeConfigValue(userConfig, configFileData, "girDirectories", options.girDirectories.default);
172
+ mergeConfigValue(userConfig, configFileData, "ignore", options.ignore.default);
173
+ mergeConfigValue(userConfig, configFileData, "modules", options.modules.default);
174
+
175
+ // Special handling for root
176
+ if (userConfig.root === options.root.default && (configFileData.root || configFile?.filepath)) {
177
+ // Use the config file path as the root path if no root path is set
178
+ userConfig.root =
179
+ configFileData.root || (configFile?.filepath ? dirname(configFile.filepath) : (options.root.default as string));
180
+ }
181
+
182
+ // Special handling for outdir
183
+ if (userConfig.outdir === options.outdir.default && configFileData.outdir) {
184
+ userConfig.outdir = userConfig.print ? null : configFileData.outdir;
185
+ }
186
+ }
187
+
188
+ // Make paths absolute
189
+ if (userConfig.outdir && !userConfig.outdir.startsWith("/")) {
190
+ userConfig.outdir = resolve(userConfig.root, userConfig.outdir);
191
+ }
192
+
193
+ if (userConfig.girDirectories) {
194
+ userConfig.girDirectories = userConfig.girDirectories.map((dir) => {
195
+ if (!dir.startsWith("/")) {
196
+ return resolve(userConfig.root, dir);
197
+ }
198
+ return dir;
199
+ });
200
+ }
201
+
202
+ return validate(userConfig);
203
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Config writer functionality for ts-for-gir
3
+ */
4
+
5
+ import { writeFile } from "node:fs/promises";
6
+ import { extname, join } from "node:path";
7
+ import type { UserConfig } from "@ts-for-gir/lib";
8
+ import { ERROR_CONFIG_EXTENSION_UNSUPPORTED, Logger, merge } from "@ts-for-gir/lib";
9
+ import { loadConfigFile } from "./config-loader.ts";
10
+ import { defaults } from "./defaults.ts";
11
+
12
+ const logger = new Logger(false, "ConfigWriter");
13
+
14
+ export let configFilePath = join(process.cwd(), defaults.configName);
15
+
16
+ /**
17
+ * Update the config file path when a config is loaded
18
+ */
19
+ export function setConfigFilePath(path: string): void {
20
+ configFilePath = path;
21
+ }
22
+
23
+ /**
24
+ * Overwrites values in the user config file
25
+ * @param configsToAdd Configuration values to add/update
26
+ * @param configName Optional custom config file name
27
+ */
28
+ export async function addToConfig(configsToAdd: Partial<UserConfig>, configName?: string): Promise<void> {
29
+ const userConfig = await loadConfigFile(configName);
30
+ const path = userConfig?.filepath || configFilePath;
31
+ const configToStore = {};
32
+ merge(configToStore, userConfig?.config || {}, configsToAdd);
33
+
34
+ const fileExtension = extname(path);
35
+ let writeConfigString = "";
36
+
37
+ switch (fileExtension) {
38
+ case ".js":
39
+ writeConfigString = `export default ${JSON.stringify(configToStore, null, 4)}`;
40
+ break;
41
+ case ".json":
42
+ writeConfigString = `${JSON.stringify(configToStore, null, 4)}`;
43
+ break;
44
+ default:
45
+ logger.error(ERROR_CONFIG_EXTENSION_UNSUPPORTED);
46
+ break;
47
+ }
48
+
49
+ if (writeConfigString && path) {
50
+ return writeFile(path, writeConfigString);
51
+ }
52
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Default configuration values for ts-for-gir CLI
3
+ */
4
+
5
+ import { existsSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ /**
9
+ * Default CLI flag and argument values
10
+ */
11
+ export const defaults = {
12
+ print: false,
13
+ configName: ".ts-for-girrc.js",
14
+ root: process.cwd(),
15
+ outdir: "./@types",
16
+ girDirectories: getDefaultGirDirectories(),
17
+ modules: ["*"],
18
+ ignore: [],
19
+ verbose: false,
20
+ ignoreVersionConflicts: false,
21
+ noNamespace: false,
22
+ noComments: false,
23
+ promisify: true,
24
+ npmScope: "@girs",
25
+ workspace: false,
26
+ onlyVersionPrefix: false,
27
+ noPrettyPrint: false,
28
+ noAdvancedVariants: false,
29
+ package: false,
30
+ reporter: false,
31
+ reporterOutput: "ts-for-gir-report.json",
32
+ };
33
+
34
+ /**
35
+ * Get default GIR directories based on the system configuration
36
+ */
37
+ function getDefaultGirDirectories(): string[] {
38
+ const girDirectories = [
39
+ "/usr/local/share/gir-1.0",
40
+ "/usr/share/gir-1.0",
41
+ "/usr/share/*/gir-1.0",
42
+ "/usr/share/gnome-shell",
43
+ "/usr/share/gnome-shell/gir-1.0",
44
+ "/usr/lib64/mutter-*",
45
+ "/usr/lib/mutter-*",
46
+ "/usr/lib/x86_64-linux-gnu/mutter-*",
47
+ ];
48
+
49
+ // NixOS and other distributions does not have a /usr/local/share directory.
50
+ // Instead, the nix store paths with Gir files are set as XDG_DATA_DIRS.
51
+ // See https://github.com/NixOS/nixpkgs/blob/96e18717904dfedcd884541e5a92bf9ff632cf39/pkgs/development/libraries/gobject-introspection/setup-hook.sh#L7-L10
52
+ const dataDirs = process.env.XDG_DATA_DIRS?.split(":") || [];
53
+ for (let dataDir of dataDirs) {
54
+ dataDir = join(dataDir, "gir-1.0");
55
+ if (!girDirectories.includes(dataDir) && existsSync(dataDir)) {
56
+ girDirectories.push(dataDir);
57
+ }
58
+ }
59
+
60
+ return girDirectories;
61
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Config module exports
3
+ */
4
+
5
+ export { getOptionsGeneration, load, validate } from "./config-loader.ts";
6
+ export { addToConfig, configFilePath } from "./config-writer.ts";
7
+ export { defaults } from "./defaults.ts";
8
+ export { analyzeOptions, copyOptions, docOptions, generateOptions, listOptions, options } from "./options.ts";