@ts-for-gir/cli 4.0.0-beta.24 → 4.0.0-beta.26

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 (62) hide show
  1. package/README.md +84 -2
  2. package/bin/ts-for-gir.js +43 -0
  3. package/package.json +30 -36
  4. package/src/commands/analyze.ts +344 -0
  5. package/src/commands/command-builder.ts +30 -0
  6. package/src/commands/copy.ts +71 -76
  7. package/src/commands/doc.ts +58 -46
  8. package/src/commands/generate.ts +97 -77
  9. package/src/commands/index.ts +6 -4
  10. package/src/commands/json.ts +104 -0
  11. package/src/commands/list.ts +81 -90
  12. package/src/config/config-loader.ts +203 -0
  13. package/src/config/config-writer.ts +52 -0
  14. package/src/config/defaults.ts +61 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/options.ts +292 -0
  17. package/src/config.ts +3 -456
  18. package/src/formatters/typescript-formatter.ts +24 -0
  19. package/src/generation-handler.ts +122 -67
  20. package/src/index.ts +4 -4
  21. package/src/module-loader/dependency-resolver.ts +100 -0
  22. package/src/module-loader/file-finder.ts +56 -0
  23. package/src/module-loader/index.ts +8 -0
  24. package/src/module-loader/module-grouper.ts +77 -0
  25. package/src/module-loader/prompt-handler.ts +111 -0
  26. package/src/module-loader.ts +280 -580
  27. package/src/start.ts +18 -14
  28. package/src/types/command-args.ts +110 -0
  29. package/src/types/command-definition.ts +15 -0
  30. package/src/types/commands.ts +35 -0
  31. package/src/types/index.ts +15 -0
  32. package/src/types/report-types.ts +34 -0
  33. package/lib/commands/copy.d.ts +0 -12
  34. package/lib/commands/copy.js +0 -78
  35. package/lib/commands/copy.js.map +0 -1
  36. package/lib/commands/doc.d.ts +0 -12
  37. package/lib/commands/doc.js +0 -38
  38. package/lib/commands/doc.js.map +0 -1
  39. package/lib/commands/generate.d.ts +0 -12
  40. package/lib/commands/generate.js +0 -70
  41. package/lib/commands/generate.js.map +0 -1
  42. package/lib/commands/index.d.ts +0 -4
  43. package/lib/commands/index.js +0 -5
  44. package/lib/commands/index.js.map +0 -1
  45. package/lib/commands/list.d.ts +0 -12
  46. package/lib/commands/list.js +0 -79
  47. package/lib/commands/list.js.map +0 -1
  48. package/lib/config.d.ts +0 -108
  49. package/lib/config.js +0 -409
  50. package/lib/config.js.map +0 -1
  51. package/lib/generation-handler.d.ts +0 -10
  52. package/lib/generation-handler.js +0 -48
  53. package/lib/generation-handler.js.map +0 -1
  54. package/lib/index.d.ts +0 -4
  55. package/lib/index.js +0 -5
  56. package/lib/index.js.map +0 -1
  57. package/lib/module-loader.d.ts +0 -154
  58. package/lib/module-loader.js +0 -465
  59. package/lib/module-loader.js.map +0 -1
  60. package/lib/start.d.ts +0 -2
  61. package/lib/start.js +0 -16
  62. package/lib/start.js.map +0 -1
@@ -2,587 +2,287 @@
2
2
  * The ModuleLoader is used for reading gir modules from the file system and to solve conflicts (e.g. Gtk-3.0 and Gtk-4.0 would be a conflict)
3
3
  */
4
4
 
5
- import { type Question } from 'inquirer'
6
- import { select } from '@inquirer/prompts'
7
- import { glob } from 'glob'
8
- import { basename, join } from 'path'
9
- import { bold } from 'colorette'
10
- import {
11
- DependencyManager,
12
- ResolveType,
13
- GirModule,
14
- Logger,
15
- splitModuleName,
16
- union,
17
- isIterable,
18
- WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE,
19
- } from '@ts-for-gir/lib'
20
- import { Config } from './config.js'
21
-
22
5
  import type {
23
- GirModulesGroupedMap,
24
- OptionsGeneration,
25
- GirModuleResolvedBy,
26
- GirModulesGrouped,
27
- DependencyMap,
28
- Dependency,
29
- AnswerVersion,
30
- } from '@ts-for-gir/lib'
6
+ AnswerVersion,
7
+ Dependency,
8
+ GirModuleResolvedBy,
9
+ GirModulesGroupedMap,
10
+ NSRegistry,
11
+ OptionsGeneration,
12
+ } from "@ts-for-gir/lib";
13
+ import {
14
+ DependencyManager,
15
+ GirModule,
16
+ Logger,
17
+ ResolveType,
18
+ union,
19
+ WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE,
20
+ } from "@ts-for-gir/lib";
21
+ import { DependencyResolver, FileFinder, ModuleGrouper, PromptHandler } from "./module-loader/index.ts";
31
22
 
32
23
  export class ModuleLoader {
33
- log: Logger
34
- dependencyManager: DependencyManager
35
- /** Transitive module dependencies */
36
- modDependencyMap: DependencyMap = {}
37
- constructor(protected readonly config: OptionsGeneration) {
38
- this.log = new Logger(config.verbose, 'ModuleLoader')
39
- this.dependencyManager = DependencyManager.getInstance(config)
40
- }
41
-
42
- /**
43
- * Groups Gir modules by name id
44
- * E.g. Gtk-3.0 and Gtk-4.0 will be grouped
45
- * @param girFiles
46
- */
47
- protected groupGirFiles(resolveGirModules: Set<GirModuleResolvedBy> | GirModuleResolvedBy[]): GirModulesGroupedMap {
48
- const girModulesGrouped: GirModulesGroupedMap = {}
49
-
50
- for (const resolveGirModule of resolveGirModules) {
51
- const { namespace } = splitModuleName(resolveGirModule.packageName)
52
- const id = namespace.toLowerCase()
53
-
54
- if (!girModulesGrouped[id]) {
55
- girModulesGrouped[id] = {
56
- namespace: namespace,
57
- modules: [resolveGirModule],
58
- hasConflict: false,
59
- }
60
- } else {
61
- girModulesGrouped[id].modules.push(resolveGirModule)
62
- girModulesGrouped[id].hasConflict = true
63
- }
64
- }
65
-
66
- return girModulesGrouped
67
- }
68
-
69
- /**
70
- * Sorts out the module the user has not selected via cli prompt
71
- * @param girModulesGrouped
72
- * @param selected Users selected module packageName
73
- */
74
- protected sortVersionsByAnswer(
75
- girModulesGrouped: GirModulesGrouped,
76
- selected: string[],
77
- ): { keep: Set<GirModuleResolvedBy>; ignore: string[] } {
78
- const keep = new Set<GirModuleResolvedBy>()
79
- let ignore: string[] = []
80
-
81
- if (!girModulesGrouped.hasConflict) {
82
- keep.add(girModulesGrouped.modules[0])
83
- } else {
84
- const keepModules = this.findGirModuleByFullNames(
85
- girModulesGrouped.modules,
86
- selected,
87
- ) as GirModuleResolvedBy[]
88
- const girModulePackageNames = girModulesGrouped.modules.map(
89
- (resolveGirModule) => resolveGirModule.packageName,
90
- )
91
- if (!keepModules || keepModules.length <= 0) {
92
- throw new Error('Module not found!')
93
- }
94
- for (const keepModule of keepModules) {
95
- keep.add(keepModule)
96
- }
97
-
98
- const toIgnore = girModulePackageNames.filter((packageName) => !selected.includes(packageName))
99
- ignore = ignore.concat(toIgnore)
100
- }
101
-
102
- return {
103
- keep,
104
- ignore,
105
- }
106
- }
107
-
108
- protected generateContinueQuestion(
109
- message = `do you want to continue?`,
110
- choices = ['Yes', 'Go back'],
111
- ): { message: string; choices: string[] } {
112
- return {
113
- message,
114
- choices,
115
- }
116
- }
117
-
118
- protected generateIgnoreDepsQuestion(
119
- message = `Do you want to ignore them too?`,
120
- choices = ['Yes', 'No', 'Go back'],
121
- ): { message: string; choices: string[] } {
122
- return {
123
- message,
124
- choices,
125
- }
126
- }
127
-
128
- protected async askIgnoreDepsPrompt(
129
- deps: GirModuleResolvedBy[] | Set<GirModuleResolvedBy>,
130
- ): Promise<'Yes' | 'No' | 'Go back'> {
131
- const size = (deps as GirModuleResolvedBy[]).length || (deps as Set<GirModuleResolvedBy>).size || 0
132
-
133
- if (size > 0) {
134
- // Show dependencies that would be ignored
135
- this.log.log(bold('\nThe following modules have the ignored modules as dependencies:'))
136
- for (const dep of deps) {
137
- this.log.log(`- ${dep.packageName}`)
138
- }
139
- this.log.log(bold('\n'))
140
-
141
- // Ask if user wants to ignore these dependencies
142
- return select<'Yes' | 'No' | 'Go back'>({
143
- message: 'Do you want to ignore them too?',
144
- choices: [
145
- { value: 'Yes', name: 'Yes' },
146
- { value: 'No', name: 'No' },
147
- { value: 'Go back', name: 'Go back' },
148
- ],
149
- })
150
- }
151
-
152
- // No dependencies found
153
- this.log.log(bold('\nNo dependencies found on the ignored modules'))
154
- return select<'Yes' | 'Go back'>({
155
- message: 'Do you want to continue?',
156
- choices: [
157
- { value: 'Yes', name: 'Yes' },
158
- { value: 'Go back', name: 'Go back' },
159
- ],
160
- })
161
- }
162
-
163
- /**
164
- * Ask for duplicates / multiple versions of a module
165
- * @param girModuleGrouped
166
- * @param message
167
- */
168
- protected generateModuleVersionQuestion(girModuleGrouped: GirModulesGrouped, message?: string): Question {
169
- message = message || `Multiple versions of '${girModuleGrouped.namespace}' found, which one do you want to use?`
170
- const choices = ['All', ...girModuleGrouped.modules.map((module) => module.packageName)]
171
-
172
- return {
173
- name: girModuleGrouped.namespace,
174
- message,
175
- type: 'list',
176
- choices,
177
- }
178
- }
179
-
180
- /**
181
- * Find modules that depend on the module with the name 'packageName'
182
- * @param girModulesGroupedMap
183
- * @param packageName
184
- */
185
- protected findGirFilesDependOnPackage(
186
- girModulesGroupedMap: GirModulesGroupedMap,
187
- packageName: string,
188
- ): GirModuleResolvedBy[] {
189
- const girModules: GirModuleResolvedBy[] = []
190
- for (const girModulesGrouped of Object.values(girModulesGroupedMap)) {
191
- for (const girModuleResolvedBy of girModulesGrouped.modules) {
192
- if (girModuleResolvedBy.packageName === packageName) {
193
- continue
194
- }
195
- for (const dep of girModuleResolvedBy.module.dependencies) {
196
- if (dep.packageName === packageName && !girModules.includes(girModuleResolvedBy)) {
197
- girModules.push(girModuleResolvedBy)
198
- }
199
- }
200
- }
201
- }
202
- return girModules
203
- }
204
-
205
- /**
206
- * Find modules that depend on the module with the names in `packageNames`
207
- * @param girModulesGroupedMap
208
- * @param packageName
209
- */
210
- protected findGirFilesDependOnPackages(
211
- girModulesGroupedMap: GirModulesGroupedMap,
212
- packageNames: string[],
213
- ): GirModuleResolvedBy[] {
214
- let girModules: GirModuleResolvedBy[] = []
215
- for (const packageName of packageNames) {
216
- girModules = [...girModules, ...this.findGirFilesDependOnPackage(girModulesGroupedMap, packageName)]
217
- }
218
- return girModules
219
- }
220
-
221
- protected async askForVersionsPrompt(girModulesGrouped: GirModulesGrouped): Promise<AnswerVersion> {
222
- const choices = ['All', ...girModulesGrouped.modules.map((module) => module.packageName)]
223
-
224
- const selected = await select<string>({
225
- message: `Multiple versions of '${girModulesGrouped.namespace}' found, which one do you want to use?`,
226
- choices: choices.map((choice) => ({
227
- value: choice,
228
- name: choice,
229
- })),
230
- })
231
-
232
- if (selected === 'All') {
233
- return {
234
- selected: choices.filter((choice) => choice !== 'All'),
235
- unselected: [],
236
- }
237
- }
238
-
239
- return {
240
- selected: [selected],
241
- unselected: choices.filter((choice) => choice !== selected && choice !== 'All'),
242
- }
243
- }
244
-
245
- /**
246
- * If multiple versions of the same module are found, this will aks the user with input prompts for the version he wish to use.
247
- * Ignores also modules that depend on a module that should be ignored
248
- * @param resolveFirModules
249
- */
250
- protected async askForEachConflictVersionsPrompt(
251
- girModulesGroupedMap: GirModulesGroupedMap,
252
- ignore: string[],
253
- ): Promise<{ keep: Set<GirModuleResolvedBy>; ignore: string[] }> {
254
- let keep = new Set<GirModuleResolvedBy>()
255
- for (const girModulesGrouped of Object.values(girModulesGroupedMap)) {
256
- // Remove ignored modules from group
257
- girModulesGrouped.modules = girModulesGrouped.modules.filter(
258
- (girGroup) => !ignore.includes(girGroup.packageName),
259
- )
260
- girModulesGrouped.hasConflict = girModulesGrouped.modules.length >= 2
261
-
262
- if (girModulesGrouped.modules.length <= 0) {
263
- continue
264
- }
265
-
266
- // Ask for version if there is a conflict
267
- if (!girModulesGrouped.hasConflict) {
268
- keep = union<GirModuleResolvedBy>(keep, girModulesGrouped.modules)
269
- } else {
270
- let goBack = true
271
- let versionAnswer: AnswerVersion | null = null
272
- let ignoreDepsAnswer: 'Yes' | 'No' | 'Go back' | null = null
273
- let wouldIgnoreDeps: GirModuleResolvedBy[] = []
274
- while (goBack) {
275
- versionAnswer = await this.askForVersionsPrompt(girModulesGrouped)
276
- // Check modules that depend on the unchosen modules
277
- wouldIgnoreDeps = this.findGirFilesDependOnPackages(girModulesGroupedMap, versionAnswer.unselected)
278
- // Do not check dependencies that have already been ignored
279
- wouldIgnoreDeps = wouldIgnoreDeps.filter((dep) => !ignore.includes(dep.packageName))
280
- ignoreDepsAnswer = await this.askIgnoreDepsPrompt(wouldIgnoreDeps)
281
- goBack = ignoreDepsAnswer === 'Go back'
282
- }
283
- if (!versionAnswer) {
284
- throw new Error('Error in processing the prompt versionAnswer')
285
- }
286
-
287
- if (ignoreDepsAnswer === 'Yes') {
288
- // Also ignore the dependencies of the unselected version
289
- ignore = ignore.concat(wouldIgnoreDeps.map((dep) => dep.packageName))
290
- }
291
-
292
- const unionMe = this.sortVersionsByAnswer(girModulesGrouped, versionAnswer.selected)
293
- // Do not ignore the selected package version
294
- keep = union<GirModuleResolvedBy>(keep, unionMe.keep)
295
- // Ignore the unchosen package versions
296
- ignore = ignore.concat(unionMe.ignore)
297
- }
298
- }
299
- if (ignore && ignore.length > 0) {
300
- const ignoreLogList = '- ' + ignore.join('\n- ')
301
-
302
- this.log.log(bold(`\n The following modules will be ignored:`))
303
- this.log.log(`\n${ignoreLogList}\n`)
304
- await this.askAddToIgnoreToConfigPrompt(ignore)
305
- }
306
-
307
- return {
308
- keep,
309
- ignore,
310
- }
311
- }
312
-
313
- /**
314
- * Asks via cli prompt if the user wants to add the ignored modules to his config file
315
- * @param ignoredModules
316
- */
317
- protected async askAddToIgnoreToConfigPrompt(ignoredModules: string[] | Set<string>): Promise<void> {
318
- const shouldAdd = await select<'Yes' | 'No'>({
319
- message: `Do you want to add the ignored modules to your config so that you don't need to select them again next time?\n Config path: '${Config.configFilePath}'`,
320
- choices: [
321
- { value: 'No', name: 'No' },
322
- { value: 'Yes', name: 'Yes' },
323
- ],
324
- })
325
-
326
- if (shouldAdd === 'Yes') {
327
- await Config.addToConfig({
328
- ignore: Array.from(ignoredModules),
329
- })
330
- this.log.log(`Add ignored modules to '${Config.configFilePath}'`)
331
- }
332
- }
333
-
334
- /**
335
- * Figure out transitive module dependencies
336
- * @param packageName
337
- * @param result
338
- */
339
- protected traverseDependencies(packageName: string, result: { [name: string]: Dependency } = {}): void {
340
- const deps = this.modDependencyMap[packageName]
341
- if (isIterable(deps)) {
342
- for (const dep of deps) {
343
- if (result[dep.packageName]) continue
344
- result[dep.packageName] = dep
345
- this.traverseDependencies(dep.packageName, result)
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * Extends the modDependencyMap by the current Module,
352
- * should be called for each girModule so that the modDependencyMap is complete
353
- * @param girModule
354
- */
355
- protected extendDependencyMapByGirModule(girModule: GirModule): void {
356
- this.modDependencyMap[girModule.packageName] = girModule.dependencies!
357
- }
358
-
359
- /**
360
- * Sets the traverse dependencies for the current girModule,
361
- * is required so that all dependencies can be found internally when generating the dependency imports for the module .d.ts file
362
- * @param girModules
363
- */
364
- protected async initGirModules(girModules: GirModuleResolvedBy[]): Promise<void> {
365
- for (const girModule of girModules) {
366
- const result: { [name: string]: Dependency } = {}
367
- this.traverseDependencies(girModule.packageName, result)
368
- await girModule.module.initTransitiveDependencies(Object.values(result))
369
- }
370
- }
371
-
372
- /**
373
- * Reads a gir xml module file and creates an object of GirModule.
374
- * Also sets the setDependencyMap
375
- * @param fillName
376
- * @param config
377
- */
378
- protected async loadAndCreateGirModule(dependency: Dependency): Promise<GirModule | null> {
379
- if (!dependency.exists || dependency.path === null) {
380
- return null
381
- }
382
-
383
- this.log.log(`Loading ${dependency.packageName}...`)
384
- const girModule = await GirModule.load(dependency, this.config, this.dependencyManager)
385
- // Figure out transitive module dependencies
386
- this.extendDependencyMapByGirModule(girModule)
387
- return girModule
388
- }
389
-
390
- /**
391
- * Returns a girModule found by `packageName` property
392
- * @param girModules Array of girModules
393
- * @param packageNames Full name like 'Gtk-3.0' you are looking for
394
- */
395
- protected findGirModuleByFullNames(
396
- girModules: (GirModuleResolvedBy | GirModule)[],
397
- packageNames: string[],
398
- ): Array<GirModuleResolvedBy | GirModule> {
399
- return girModules.filter((girModule) => packageNames.includes(girModule.packageName))
400
- }
401
-
402
- /**
403
- * Checks if a girModules with the `packageNames` exists
404
- * @param girModules
405
- * @param packageName
406
- */
407
- protected existsGirModules(girModules: (GirModuleResolvedBy | GirModule)[], packageName: string): boolean {
408
- const foundModule = this.findGirModuleByFullNames(girModules, [packageName])
409
- return foundModule.length > 0
410
- }
411
-
412
- /**
413
- * Reads the gir xml module files and creates an object of GirModule for each module
414
- * @param dependencies
415
- * @param girModules
416
- * @param resolvedBy
417
- * @param failedGirModules
418
- * @param ignoreDependencies
419
- * @returns
420
- */
421
- protected async loadGirModules(
422
- dependencies: Dependency[],
423
- ignoreDependencies: string[] = [],
424
- girModules: GirModuleResolvedBy[] = [],
425
- resolvedBy = ResolveType.BY_HAND,
426
- failedGirModules = new Set<string>(),
427
- ): Promise<{ loaded: GirModuleResolvedBy[]; failed: Set<string> }> {
428
- let newModuleFound = false
429
-
430
- // Clone array
431
- dependencies = [...dependencies]
432
-
433
- while (dependencies.length > 0) {
434
- const dependency = dependencies.shift()
435
- if (!dependency?.packageName) continue
436
- // If module has not already been loaded
437
- if (!this.existsGirModules(girModules, dependency.packageName)) {
438
- const girModule = await this.loadAndCreateGirModule(dependency)
439
- if (!girModule) {
440
- if (!failedGirModules.has(dependency.packageName)) {
441
- this.log.warn(WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE(dependency.packageName))
442
- failedGirModules.add(dependency.packageName)
443
- }
444
- } else if (girModule && girModule.packageName) {
445
- const addModule = {
446
- packageName: girModule.packageName,
447
- module: girModule,
448
- resolvedBy,
449
- path: dependency.path,
450
- }
451
- girModules.push(addModule)
452
- newModuleFound = true
453
- }
454
- }
455
- }
456
-
457
- if (!newModuleFound) {
458
- return {
459
- loaded: girModules,
460
- failed: failedGirModules,
461
- }
462
- }
463
-
464
- // Figure out transitive module dependencies
465
- await this.initGirModules(girModules)
466
-
467
- // Load girModules for dependencies
468
- for (const girModule of girModules) {
469
- // Load dependencies
470
- const transitiveDependencies = girModule.module.transitiveDependencies
471
- if (transitiveDependencies.length > 0) {
472
- await this.loadGirModules(
473
- transitiveDependencies,
474
- ignoreDependencies,
475
- girModules,
476
- ResolveType.DEPENDENCE,
477
- failedGirModules,
478
- )
479
- }
480
- }
481
-
482
- return {
483
- loaded: girModules,
484
- failed: failedGirModules,
485
- }
486
- }
487
-
488
- /**
489
- * Find modules with the possibility to use wild cards for module names. E.g. `Gtk*` or `'*'`
490
- * @param modules
491
- * @param ignore
492
- */
493
- protected async findGirFiles(globPackageNames: string[], ignore: string[] = []): Promise<Set<string>> {
494
- const foundFiles = new Set<string>()
495
-
496
- for (let i = 0; i < globPackageNames.length; i++) {
497
- if (!globPackageNames[i]) {
498
- continue
499
- }
500
- const filename = `${globPackageNames[i]}.gir`
501
- const pattern = this.config.girDirectories.map((girDirectory) => join(girDirectory, filename))
502
- const ignoreGirs = ignore.map((girDirectory) => girDirectory + '.gir')
503
- const files = await glob(pattern, { ignore: ignoreGirs })
504
- files.forEach((file) => foundFiles.add(file))
505
- }
506
-
507
- return foundFiles
508
- }
509
-
510
- protected async girFilePathToDependencies(girFiles: Set<string>): Promise<Dependency[]> {
511
- const dependencies: Dependency[] = []
512
- for (const girFile of girFiles) {
513
- const packageName = basename(girFile, '.gir')
514
- const { namespace, version } = splitModuleName(packageName)
515
- const dep = await this.dependencyManager.get(namespace, version)
516
- dependencies.push(dep)
517
- }
518
-
519
- return dependencies
520
- }
521
-
522
- /**
523
- * Loads all found `packageNames`
524
- * @param girDirectories
525
- * @param packageNames
526
- * @param doNotAskForVersionOnConflict Set this to false if you want to get a prompt for each version conflict
527
- */
528
- public async getModulesResolved(
529
- packageNames: string[],
530
- ignore: string[] = [],
531
- doNotAskForVersionOnConflict = true,
532
- ): Promise<{ keep: GirModuleResolvedBy[]; grouped: GirModulesGroupedMap; ignore: string[]; failed: Set<string> }> {
533
- const girFiles = await this.findGirFiles([...packageNames], ignore)
534
- // Always require these because GJS does...
535
- const GLib = await this.dependencyManager.get('GLib', '2.0')
536
- const Gio = await this.dependencyManager.get('Gio', '2.0')
537
- const GObject = await this.dependencyManager.get('GObject', '2.0')
538
-
539
- const dependencies = await this.girFilePathToDependencies(girFiles)
540
-
541
- const { loaded, failed } = await this.loadGirModules(
542
- [
543
- GLib,
544
- Gio,
545
- GObject,
546
- ...dependencies.filter(
547
- (dep) => dep.namespace !== 'GLib' && dep.namespace !== 'Gio' && dep.namespace !== 'GObject',
548
- ),
549
- ],
550
- ignore,
551
- )
552
- let keep: GirModuleResolvedBy[] = []
553
- if (doNotAskForVersionOnConflict) {
554
- keep = loaded
555
- } else {
556
- const girModulesGrouped = this.groupGirFiles(loaded)
557
- const filtered = await this.askForEachConflictVersionsPrompt(girModulesGrouped, ignore)
558
- keep = Array.from(filtered.keep)
559
- }
560
-
561
- const grouped = this.groupGirFiles(keep)
562
-
563
- return { keep, grouped, ignore, failed }
564
- }
565
-
566
- /**
567
- * Find modules
568
- * @param girDirectories
569
- * @param modules
570
- */
571
- public async getModules(
572
- modules: string[],
573
- ignore: string[] = [],
574
- ): Promise<{ grouped: GirModulesGroupedMap; loaded: GirModuleResolvedBy[]; failed: string[] }> {
575
- const girFiles = await this.findGirFiles(modules, ignore)
576
- const dependencies = await this.girFilePathToDependencies(girFiles)
577
- const { loaded, failed } = await this.loadGirModules(dependencies, ignore)
578
- const grouped = this.groupGirFiles(loaded)
579
- return { grouped, loaded, failed: Array.from(failed) }
580
- }
581
-
582
- /** Start parsing the gir modules */
583
- public parse(girModules: GirModuleResolvedBy[]): void {
584
- for (const girModule of girModules) {
585
- girModule.module.parse()
586
- }
587
- }
24
+ private readonly log: Logger;
25
+ private readonly dependencyManager: DependencyManager;
26
+ private readonly dependencyResolver: DependencyResolver;
27
+ private readonly fileFinder: FileFinder;
28
+ private readonly moduleGrouper: ModuleGrouper;
29
+ private readonly promptHandler: PromptHandler;
30
+
31
+ constructor(
32
+ private readonly config: OptionsGeneration,
33
+ private readonly registry: NSRegistry,
34
+ ) {
35
+ this.log = new Logger(config.verbose, "ModuleLoader");
36
+ this.dependencyManager = DependencyManager.getInstance(config);
37
+ this.dependencyResolver = new DependencyResolver();
38
+ this.fileFinder = new FileFinder(config.girDirectories, this.dependencyManager);
39
+ this.moduleGrouper = new ModuleGrouper();
40
+ this.promptHandler = new PromptHandler(config.verbose);
41
+ }
42
+
43
+ /**
44
+ * Sets the traverse dependencies for the current girModule,
45
+ * is required so that all dependencies can be found internally when generating the dependency imports for the module .d.ts file
46
+ */
47
+ private async initGirModules(girModules: GirModuleResolvedBy[]): Promise<void> {
48
+ for (const girModule of girModules) {
49
+ const dependencies = this.dependencyResolver.getTransitiveDependencies(girModule.packageName);
50
+ await girModule.module.initTransitiveDependencies(dependencies);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Reads a gir xml module file and creates an object of GirModule.
56
+ * Also sets the setDependencyMap
57
+ */
58
+ private async loadAndCreateGirModule(dependency: Dependency): Promise<GirModule | null> {
59
+ if (!dependency.exists || dependency.path === null) {
60
+ return null;
61
+ }
62
+
63
+ this.log.log(`Loading ${dependency.packageName}...`);
64
+ const girModule = await GirModule.load(dependency, this.config, this.registry);
65
+ // Figure out transitive module dependencies
66
+ this.dependencyResolver.extendDependencyMapByGirModule(girModule);
67
+ return girModule;
68
+ }
69
+
70
+ /**
71
+ * If multiple versions of the same module are found, this will ask the user with input prompts for the version they wish to use.
72
+ * Ignores also modules that depend on a module that should be ignored
73
+ */
74
+ private async askForEachConflictVersionsPrompt(
75
+ girModulesGroupedMap: GirModulesGroupedMap,
76
+ ignore: string[],
77
+ ): Promise<{ keep: Set<GirModuleResolvedBy>; ignore: string[] }> {
78
+ let keep = new Set<GirModuleResolvedBy>();
79
+
80
+ for (const girModulesGrouped of Object.values(girModulesGroupedMap)) {
81
+ // Remove ignored modules from group
82
+ girModulesGrouped.modules = girModulesGrouped.modules.filter(
83
+ (girGroup) => !ignore.includes(girGroup.packageName),
84
+ );
85
+ girModulesGrouped.hasConflict = girModulesGrouped.modules.length >= 2;
86
+
87
+ if (girModulesGrouped.modules.length <= 0) {
88
+ continue;
89
+ }
90
+
91
+ // Ask for version if there is a conflict
92
+ if (!girModulesGrouped.hasConflict) {
93
+ keep = union<GirModuleResolvedBy>(keep, girModulesGrouped.modules);
94
+ } else {
95
+ let goBack = true;
96
+ let versionAnswer: AnswerVersion | null = null;
97
+ let ignoreDepsAnswer: "Yes" | "No" | "Go back" | null = null;
98
+ let wouldIgnoreDeps: GirModuleResolvedBy[] = [];
99
+
100
+ while (goBack) {
101
+ versionAnswer = await this.promptHandler.askForVersionsPrompt(girModulesGrouped);
102
+ // Check modules that depend on the unchosen modules
103
+ wouldIgnoreDeps = this.dependencyResolver.findModulesDependingOnPackages(
104
+ girModulesGroupedMap,
105
+ versionAnswer.unselected,
106
+ );
107
+ // Do not check dependencies that have already been ignored
108
+ wouldIgnoreDeps = wouldIgnoreDeps.filter((dep) => !ignore.includes(dep.packageName));
109
+ ignoreDepsAnswer = await this.promptHandler.askIgnoreDepsPrompt(wouldIgnoreDeps);
110
+ goBack = ignoreDepsAnswer === "Go back";
111
+ }
112
+
113
+ if (!versionAnswer) {
114
+ throw new Error("Error in processing the prompt versionAnswer");
115
+ }
116
+
117
+ if (ignoreDepsAnswer === "Yes") {
118
+ // Also ignore the dependencies of the unselected version
119
+ ignore = ignore.concat(wouldIgnoreDeps.map((dep) => dep.packageName));
120
+ }
121
+
122
+ const unionMe = this.moduleGrouper.sortVersionsByAnswer(girModulesGrouped, versionAnswer.selected);
123
+ // Do not ignore the selected package version
124
+ keep = union<GirModuleResolvedBy>(keep, unionMe.keep);
125
+ // Ignore the unchosen package versions
126
+ ignore = ignore.concat(unionMe.ignore);
127
+ }
128
+ }
129
+
130
+ if (ignore && ignore.length > 0) {
131
+ this.promptHandler.showIgnoredModules(ignore);
132
+ await this.promptHandler.askAddToIgnoreToConfigPrompt(ignore);
133
+ }
134
+
135
+ return {
136
+ keep,
137
+ ignore,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Reads the gir xml module files and creates an object of GirModule for each module
143
+ */
144
+ private async loadGirModules(
145
+ dependencies: Dependency[],
146
+ ignoreDependencies: string[] = [],
147
+ girModules: GirModuleResolvedBy[] = [],
148
+ resolvedBy = ResolveType.BY_HAND,
149
+ failedGirModules = new Set<string>(),
150
+ ): Promise<{ loaded: GirModuleResolvedBy[]; failed: Set<string> }> {
151
+ let newModuleFound = false;
152
+
153
+ // Clone array
154
+ dependencies = [...dependencies];
155
+
156
+ while (dependencies.length > 0) {
157
+ const dependency = dependencies.shift();
158
+ if (!dependency?.packageName) continue;
159
+ // If module has not already been loaded
160
+ if (!this.dependencyResolver.existsGirModules(girModules, dependency.packageName)) {
161
+ const girModule = await this.loadAndCreateGirModule(dependency);
162
+ if (!girModule) {
163
+ if (!failedGirModules.has(dependency.packageName)) {
164
+ this.log.warn(WARN_NO_GIR_FILE_FOUND_FOR_PACKAGE(dependency.packageName));
165
+ failedGirModules.add(dependency.packageName);
166
+ }
167
+ } else if (girModule?.packageName) {
168
+ const addModule = {
169
+ namespace: dependency.namespace,
170
+ version: dependency.version,
171
+ packageName: girModule.packageName,
172
+ module: girModule,
173
+ resolvedBy,
174
+ path: dependency.path,
175
+ };
176
+ girModules.push(addModule);
177
+ newModuleFound = true;
178
+ }
179
+ }
180
+ }
181
+
182
+ if (!newModuleFound) {
183
+ return {
184
+ loaded: girModules,
185
+ failed: failedGirModules,
186
+ };
187
+ }
188
+
189
+ // Figure out transitive module dependencies
190
+ await this.initGirModules(girModules);
191
+
192
+ // Load girModules for dependencies
193
+ for (const girModule of girModules) {
194
+ // Load dependencies
195
+ const transitiveDependencies = girModule.module.transitiveDependencies;
196
+ if (transitiveDependencies.length > 0) {
197
+ await this.loadGirModules(
198
+ transitiveDependencies,
199
+ ignoreDependencies,
200
+ girModules,
201
+ ResolveType.DEPENDENCE,
202
+ failedGirModules,
203
+ );
204
+ }
205
+ }
206
+
207
+ return {
208
+ loaded: girModules,
209
+ failed: failedGirModules,
210
+ };
211
+ }
212
+
213
+ /**
214
+ * Loads all found `packageNames`
215
+ * @param packageNames Module names to load
216
+ * @param ignore Modules to ignore
217
+ * @param doNotAskForVersionOnConflict Set this to false if you want to get a prompt for each version conflict
218
+ */
219
+ public async getModulesResolved(
220
+ packageNames: string[],
221
+ ignore: string[] = [],
222
+ doNotAskForVersionOnConflict = true,
223
+ ): Promise<{ keep: GirModuleResolvedBy[]; grouped: GirModulesGroupedMap; ignore: string[]; failed: Set<string> }> {
224
+ const girFiles = await this.fileFinder.findGirFiles([...packageNames], ignore);
225
+ // Always require these because GJS does...
226
+ const GLib = await this.dependencyManager.get("GLib", "2.0");
227
+ const Gio = await this.dependencyManager.get("Gio", "2.0");
228
+ const GObject = await this.dependencyManager.get("GObject", "2.0");
229
+ const Cairo = await this.dependencyManager.get("cairo", "1.0");
230
+
231
+ const dependencies = await this.fileFinder.girFilePathToDependencies(girFiles);
232
+
233
+ const { loaded, failed } = await this.loadGirModules(
234
+ [
235
+ GLib,
236
+ Gio,
237
+ GObject,
238
+ Cairo,
239
+ ...dependencies.filter(
240
+ (dep) =>
241
+ dep.namespace !== "GLib" &&
242
+ dep.namespace !== "Gio" &&
243
+ dep.namespace !== "GObject" &&
244
+ dep.namespace !== "cairo",
245
+ ),
246
+ ],
247
+ ignore,
248
+ );
249
+
250
+ let keep: GirModuleResolvedBy[] = [];
251
+ if (doNotAskForVersionOnConflict) {
252
+ keep = loaded;
253
+ } else {
254
+ const girModulesGrouped = this.moduleGrouper.groupGirFiles(loaded);
255
+ const filtered = await this.askForEachConflictVersionsPrompt(girModulesGrouped, ignore);
256
+ keep = Array.from(filtered.keep);
257
+ }
258
+
259
+ const grouped = this.moduleGrouper.groupGirFiles(keep);
260
+
261
+ return { keep, grouped, ignore, failed };
262
+ }
263
+
264
+ /**
265
+ * Find modules
266
+ * @param modules Module names to find
267
+ * @param ignore Modules to ignore
268
+ */
269
+ public async getModules(
270
+ modules: string[],
271
+ ignore: string[] = [],
272
+ ): Promise<{ grouped: GirModulesGroupedMap; loaded: GirModuleResolvedBy[]; failed: string[] }> {
273
+ const girFiles = await this.fileFinder.findGirFiles(modules, ignore);
274
+ const dependencies = await this.fileFinder.girFilePathToDependencies(girFiles);
275
+ const { loaded, failed } = await this.loadGirModules(dependencies, ignore);
276
+ const grouped = this.moduleGrouper.groupGirFiles(loaded);
277
+ return { grouped, loaded, failed: Array.from(failed) };
278
+ }
279
+
280
+ /**
281
+ * Start parsing the gir modules
282
+ */
283
+ public parse(girModules: GirModuleResolvedBy[]): void {
284
+ for (const girModule of girModules) {
285
+ girModule.module.parse();
286
+ }
287
+ }
588
288
  }