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