@cyberismo/data-handler 0.0.5 → 0.0.7

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 (157) hide show
  1. package/README.md +1 -0
  2. package/dist/card-metadata-updater.d.ts +1 -0
  3. package/dist/card-metadata-updater.js +7 -2
  4. package/dist/card-metadata-updater.js.map +1 -1
  5. package/dist/command-handler.d.ts +6 -1
  6. package/dist/command-handler.js +16 -15
  7. package/dist/command-handler.js.map +1 -1
  8. package/dist/command-manager.d.ts +15 -4
  9. package/dist/command-manager.js +41 -9
  10. package/dist/command-manager.js.map +1 -1
  11. package/dist/commands/calculate.d.ts +4 -10
  12. package/dist/commands/calculate.js +67 -78
  13. package/dist/commands/calculate.js.map +1 -1
  14. package/dist/commands/export.js +3 -3
  15. package/dist/commands/export.js.map +1 -1
  16. package/dist/commands/import.d.ts +3 -8
  17. package/dist/commands/import.js +10 -14
  18. package/dist/commands/import.js.map +1 -1
  19. package/dist/commands/index.d.ts +1 -2
  20. package/dist/commands/index.js +1 -2
  21. package/dist/commands/index.js.map +1 -1
  22. package/dist/commands/remove.js +1 -1
  23. package/dist/commands/remove.js.map +1 -1
  24. package/dist/commands/show.d.ts +6 -3
  25. package/dist/commands/show.js +8 -5
  26. package/dist/commands/show.js.map +1 -1
  27. package/dist/commands/validate.js +6 -4
  28. package/dist/commands/validate.js.map +1 -1
  29. package/dist/containers/project/project-content-watcher.d.ts +28 -0
  30. package/dist/containers/project/project-content-watcher.js +54 -0
  31. package/dist/containers/project/project-content-watcher.js.map +1 -0
  32. package/dist/containers/project/project-paths.js +1 -1
  33. package/dist/containers/project/project-paths.js.map +1 -1
  34. package/dist/containers/project.d.ts +9 -2
  35. package/dist/containers/project.js +49 -1
  36. package/dist/containers/project.js.map +1 -1
  37. package/dist/containers/template.d.ts +1 -0
  38. package/dist/containers/template.js +7 -2
  39. package/dist/containers/template.js.map +1 -1
  40. package/dist/index.d.ts +2 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/interfaces/macros.d.ts +2 -1
  43. package/dist/interfaces/project-interfaces.d.ts +5 -0
  44. package/dist/interfaces/project-interfaces.js.map +1 -1
  45. package/dist/interfaces/resource-interfaces.d.ts +14 -22
  46. package/dist/interfaces/resource-interfaces.js +10 -9
  47. package/dist/interfaces/resource-interfaces.js.map +1 -1
  48. package/dist/macros/graph/index.d.ts +1 -1
  49. package/dist/macros/graph/index.js +12 -12
  50. package/dist/macros/graph/index.js.map +1 -1
  51. package/dist/macros/index.d.ts +24 -3
  52. package/dist/macros/index.js +11 -4
  53. package/dist/macros/index.js.map +1 -1
  54. package/dist/macros/report/index.d.ts +13 -10
  55. package/dist/macros/report/index.js +26 -38
  56. package/dist/macros/report/index.js.map +1 -1
  57. package/dist/module-manager.d.ts +16 -7
  58. package/dist/module-manager.js +142 -112
  59. package/dist/module-manager.js.map +1 -1
  60. package/dist/project-settings.js +6 -0
  61. package/dist/project-settings.js.map +1 -1
  62. package/dist/resources/array-handler.js +6 -1
  63. package/dist/resources/array-handler.js.map +1 -1
  64. package/dist/resources/card-type-resource.d.ts +13 -9
  65. package/dist/resources/card-type-resource.js +47 -23
  66. package/dist/resources/card-type-resource.js.map +1 -1
  67. package/dist/resources/create-defaults.d.ts +10 -9
  68. package/dist/resources/create-defaults.js +15 -12
  69. package/dist/resources/create-defaults.js.map +1 -1
  70. package/dist/resources/field-type-resource.d.ts +0 -1
  71. package/dist/resources/field-type-resource.js +2 -10
  72. package/dist/resources/field-type-resource.js.map +1 -1
  73. package/dist/resources/file-resource.d.ts +7 -7
  74. package/dist/resources/file-resource.js +32 -7
  75. package/dist/resources/file-resource.js.map +1 -1
  76. package/dist/resources/folder-resource.d.ts +10 -9
  77. package/dist/resources/folder-resource.js +10 -9
  78. package/dist/resources/folder-resource.js.map +1 -1
  79. package/dist/resources/report-resource.d.ts +5 -6
  80. package/dist/resources/report-resource.js +16 -7
  81. package/dist/resources/report-resource.js.map +1 -1
  82. package/dist/resources/template-resource.d.ts +5 -6
  83. package/dist/resources/template-resource.js +7 -6
  84. package/dist/resources/template-resource.js.map +1 -1
  85. package/dist/resources/workflow-resource.d.ts +15 -8
  86. package/dist/resources/workflow-resource.js +124 -8
  87. package/dist/resources/workflow-resource.js.map +1 -1
  88. package/dist/types/queries.d.ts +11 -10
  89. package/dist/types/queries.js +10 -9
  90. package/dist/types/queries.js.map +1 -1
  91. package/dist/utils/clingo-fact-builder.d.ts +1 -0
  92. package/dist/utils/clingo-fact-builder.js +8 -3
  93. package/dist/utils/clingo-fact-builder.js.map +1 -1
  94. package/dist/utils/clingo-facts.js +15 -11
  95. package/dist/utils/clingo-facts.js.map +1 -1
  96. package/dist/utils/constants.d.ts +18 -12
  97. package/dist/utils/constants.js +18 -11
  98. package/dist/utils/constants.js.map +1 -1
  99. package/dist/utils/log-utils.d.ts +15 -2
  100. package/dist/utils/log-utils.js +20 -37
  101. package/dist/utils/log-utils.js.map +1 -1
  102. package/dist/utils/report.d.ts +27 -0
  103. package/dist/utils/report.js +60 -0
  104. package/dist/utils/report.js.map +1 -0
  105. package/dist/utils/resource-utils.js +3 -0
  106. package/dist/utils/resource-utils.js.map +1 -1
  107. package/dist/utils/sanitize-svg.d.ts +3 -4
  108. package/dist/utils/sanitize-svg.js +4 -7
  109. package/dist/utils/sanitize-svg.js.map +1 -1
  110. package/dist/utils/validate.js +2 -1
  111. package/dist/utils/validate.js.map +1 -1
  112. package/package.json +9 -10
  113. package/src/card-metadata-updater.ts +7 -2
  114. package/src/command-handler.ts +23 -13
  115. package/src/command-manager.ts +54 -13
  116. package/src/commands/calculate.ts +90 -106
  117. package/src/commands/export.ts +3 -2
  118. package/src/commands/import.ts +16 -16
  119. package/src/commands/index.ts +0 -2
  120. package/src/commands/remove.ts +1 -1
  121. package/src/commands/show.ts +13 -5
  122. package/src/commands/validate.ts +14 -9
  123. package/src/containers/project/project-content-watcher.ts +65 -0
  124. package/src/containers/project/project-paths.ts +1 -1
  125. package/src/containers/project.ts +60 -1
  126. package/src/containers/template.ts +7 -2
  127. package/src/index.ts +2 -0
  128. package/src/interfaces/macros.ts +2 -1
  129. package/src/interfaces/project-interfaces.ts +8 -0
  130. package/src/interfaces/resource-interfaces.ts +15 -22
  131. package/src/macros/graph/index.ts +17 -12
  132. package/src/macros/index.ts +32 -6
  133. package/src/macros/report/index.ts +32 -42
  134. package/src/module-manager.ts +183 -139
  135. package/src/project-settings.ts +7 -0
  136. package/src/resources/array-handler.ts +7 -2
  137. package/src/resources/card-type-resource.ts +61 -46
  138. package/src/resources/create-defaults.ts +15 -12
  139. package/src/resources/field-type-resource.ts +2 -17
  140. package/src/resources/file-resource.ts +46 -8
  141. package/src/resources/folder-resource.ts +11 -10
  142. package/src/resources/report-resource.ts +19 -7
  143. package/src/resources/template-resource.ts +7 -6
  144. package/src/resources/workflow-resource.ts +155 -8
  145. package/src/types/queries.ts +11 -10
  146. package/src/utils/clingo-fact-builder.ts +8 -3
  147. package/src/utils/clingo-facts.ts +18 -12
  148. package/src/utils/constants.ts +20 -12
  149. package/src/utils/log-utils.ts +24 -45
  150. package/src/utils/report.ts +86 -0
  151. package/src/utils/resource-utils.ts +4 -0
  152. package/src/utils/sanitize-svg.ts +4 -9
  153. package/src/utils/validate.ts +3 -2
  154. package/dist/commands/export-site.d.ts +0 -45
  155. package/dist/commands/export-site.js +0 -301
  156. package/dist/commands/export-site.js.map +0 -1
  157. package/src/commands/export-site.ts +0 -356
@@ -1,35 +1,39 @@
1
1
  /**
2
- Cyberismo
3
- Copyright © Cyberismo Ltd and contributors 2024
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
-
7
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
-
9
- You should have received a copy of the GNU Affero General Public
10
- License along with this program. If not, see <https://www.gnu.org/licenses/>.
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
12
  */
12
13
 
13
- import { registerEmptyMacros, validateMacroContent } from '../index.js';
14
+ import { validateMacroContent } from '../index.js';
14
15
  import type { MacroOptions } from '../index.js';
16
+
15
17
  import type { MacroGenerationContext } from '../../interfaces/macros.js';
16
18
  import macroMetadata from './metadata.js';
17
- import { Project } from '../../containers/project.js';
18
- import { Calculate } from '../../commands/index.js';
19
- import Handlebars from 'handlebars';
19
+ import type { Calculate } from '../../commands/index.js';
20
20
  import BaseMacro from '../base-macro.js';
21
21
  import { validateJson } from '../../utils/validate.js';
22
22
  import type TaskQueue from '../task-queue.js';
23
23
  import { ReportResource } from '../../resources/report-resource.js';
24
24
  import { resourceName } from '../../utils/resource-utils.js';
25
+ import { generateReportContent } from '../../utils/report.js';
25
26
 
26
27
  export interface ReportOptions extends MacroOptions {
27
28
  name: string;
28
29
  }
29
30
 
30
31
  class ReportMacro extends BaseMacro {
31
- constructor(tasksQueue: TaskQueue) {
32
- super(macroMetadata, tasksQueue);
32
+ constructor(
33
+ tasks: TaskQueue,
34
+ private readonly calculate: Calculate,
35
+ ) {
36
+ super(macroMetadata, tasks);
33
37
  }
34
38
  handleValidate = (input: unknown) => {
35
39
  this.validate(input);
@@ -41,11 +45,13 @@ class ReportMacro extends BaseMacro {
41
45
 
42
46
  handleInject = async (context: MacroGenerationContext, data: unknown) => {
43
47
  const options = this.validate(data);
44
- const project = new Project(context.projectPath);
45
- const resource = new ReportResource(project, resourceName(options.name));
48
+ const resource = new ReportResource(
49
+ context.project,
50
+ resourceName(options.name),
51
+ );
46
52
  const report = await resource.show();
47
53
 
48
- if (!report) throw new Error(`Report ${options} does not exist`);
54
+ if (!report) throw new Error(`Report ${options.name} does not exist`);
49
55
 
50
56
  if (report.schema) {
51
57
  validateJson(options, {
@@ -53,30 +59,14 @@ class ReportMacro extends BaseMacro {
53
59
  });
54
60
  }
55
61
 
56
- const handlebarsContext = {
57
- cardKey: context.cardKey,
58
- ...options,
59
- };
60
-
61
- const handlebars = Handlebars.create();
62
-
63
- const template = handlebars.compile(report.queryTemplate, {
64
- strict: true,
65
- });
66
-
67
- const calculate = new Calculate(project);
68
- const result = await calculate.runLogicProgram({
69
- query: template(handlebarsContext),
70
- });
71
- if (result.error) {
72
- throw new Error(result.error);
73
- }
74
- // register empty macros so that other macros aren't touched yet
75
- registerEmptyMacros(handlebars);
76
-
77
- return handlebars.compile(report.contentTemplate)({
78
- ...handlebarsContext,
79
- ...result,
62
+ return generateReportContent({
63
+ calculate: this.calculate,
64
+ contentTemplate: report.contentTemplate,
65
+ queryTemplate: report.queryTemplate,
66
+ options: {
67
+ cardKey: context.cardKey,
68
+ ...options,
69
+ },
80
70
  });
81
71
  };
82
72
 
@@ -11,16 +11,14 @@
11
11
  License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
12
  */
13
13
 
14
- import fs from 'node:fs';
15
14
  import { join } from 'node:path';
16
15
  import { mkdir, readdir, rm } from 'node:fs/promises';
17
16
 
18
- import git from 'isomorphic-git';
19
- import http from 'isomorphic-git/http/node/index.js';
17
+ import { simpleGit, type SimpleGit } from 'simple-git';
20
18
 
21
19
  import { copyDir, deleteDir, pathExists } from './utils/file-utils.js';
22
- import type { Import } from './commands/index.js';
23
20
  import type {
21
+ Credentials,
24
22
  ModuleSetting,
25
23
  ModuleSettingOptions,
26
24
  } from './interfaces/project-interfaces.js';
@@ -33,6 +31,8 @@ import { Validate } from './commands/index.js';
33
31
  const FILE_PROTOCOL = 'file:';
34
32
  // todo: add support for git's default branch.
35
33
  const MAIN_BRANCH = 'main';
34
+ // timeout in milliseconds for git client (no stdout / stderr activity)
35
+ const DEFAULT_TIMEOUT = 10000;
36
36
 
37
37
  /**
38
38
  * Class that handles module updates and imports.
@@ -40,10 +40,7 @@ const MAIN_BRANCH = 'main';
40
40
  export class ModuleManager {
41
41
  private modules: ModuleSetting[] = [];
42
42
  private tempModulesDir: string = '';
43
- constructor(
44
- private project: Project,
45
- private importCmd: Import,
46
- ) {
43
+ constructor(private project: Project) {
47
44
  this.tempModulesDir = join(this.project.paths.tempFolder, 'modules');
48
45
  }
49
46
 
@@ -56,87 +53,82 @@ export class ModuleManager {
56
53
  await this.project.collectModuleResources();
57
54
  }
58
55
 
59
- // Handles a branch of a repository.
60
- private async branch(module: ModuleSetting) {
61
- if (module.branch === MAIN_BRANCH || module.branch === '' || !module.branch)
62
- return;
63
-
64
- await git.checkout({
65
- fs,
66
- dir: join(this.tempModulesDir, module.name),
67
- ref: module.branch,
68
- });
69
- console.error(
70
- `... Switched to '${module.branch}' branch for module '${module.name}'`,
71
- );
72
- }
73
-
74
56
  // Handles cloning of a repository.
75
57
  private async clone(
76
58
  module: ModuleSetting,
77
59
  verbose: boolean = true,
60
+ credentials?: Credentials,
78
61
  ): Promise<string> {
79
62
  if (!module.name || module.name === '') {
80
63
  module.name = this.repositoryName(module.location);
81
64
  }
82
65
 
83
- let repoUrl: URL;
84
- try {
85
- repoUrl = new URL(module.location);
86
- } catch {
87
- throw new Error(`Invalid repository URL: ${module.location}`);
66
+ const destinationPath = join(this.tempModulesDir, module.name);
67
+
68
+ let remote = module.location;
69
+ if (module.private) {
70
+ if (credentials && credentials?.username && credentials?.token) {
71
+ if (verbose) {
72
+ console.log(
73
+ `... Using credentials '${credentials?.username}' for cloning '${module.name}'`,
74
+ );
75
+ }
76
+ try {
77
+ const repoUrl = new URL(module.location);
78
+ const user = credentials?.username;
79
+ const pass = credentials?.token;
80
+ const host = repoUrl.host;
81
+ const path = repoUrl.pathname;
82
+ remote = `https://${user}:${pass}@${host}${path}`;
83
+ } catch {
84
+ throw new Error(`Invalid repository URL: ${module.location}`);
85
+ }
86
+ } else {
87
+ if (verbose) {
88
+ console.log(`... Not using credentials for cloning '${module.name}'`);
89
+ }
90
+ }
88
91
  }
89
92
 
90
- if (
91
- process.env.CYBERISMO_GIT_USER &&
92
- process.env.CYBERISMO_GIT_TOKEN &&
93
- module.private
94
- ) {
93
+ try {
94
+ await mkdir(this.tempModulesDir, { recursive: true });
95
+ const cloneOptions = this.setCloneOptions(module);
96
+ await rm(destinationPath, { recursive: true, force: true });
97
+
98
+ const git: SimpleGit = simpleGit({
99
+ timeout: {
100
+ block: DEFAULT_TIMEOUT,
101
+ },
102
+ });
103
+
104
+ await git
105
+ // turn off git prompts
106
+ .env({ GIT_TERMINAL_PROMPT: 0, GCM_INTERACTIVE: 'never' })
107
+ .clone(remote, destinationPath, cloneOptions);
108
+
95
109
  if (verbose) {
96
- console.log(
97
- `... Using credentials '${process.env.CYBERISMO_GIT_USER}' for cloning '${module.name}'`,
98
- );
110
+ console.log(`... Cloned '${module.name}' to a temporary folder`);
99
111
  }
100
- repoUrl.username = process.env.CYBERISMO_GIT_USER;
101
- repoUrl.password = process.env.CYBERISMO_GIT_TOKEN;
112
+ } catch (error) {
113
+ console.error(error);
114
+ if (error instanceof Error)
115
+ throw new Error(
116
+ `Failed to clone module '${module.name}': ${error.message}`,
117
+ );
102
118
  }
103
- await git.clone({
104
- fs,
105
- http,
106
- dir: join(this.tempModulesDir, module.name),
107
- url: repoUrl.toString(),
108
- depth: 1,
109
- onAuth: () => {
110
- // Turn credentials 'off' when they are not available
111
- if (
112
- !process.env.CYBERISMO_GIT_USER ||
113
- !process.env.CYBERISMO_GIT_TOKEN
114
- ) {
115
- return undefined;
116
- }
117
- // Turn credentials 'off' for public repos
118
- if (!module.private) {
119
- return undefined;
120
- }
121
- return {
122
- username: process.env.CYBERISMO_GIT_USER,
123
- password: process.env.CYBERISMO_GIT_TOKEN,
124
- };
125
- },
126
- });
127
119
 
128
- if (verbose) {
129
- console.log(`... Cloned '${module.name}' to a temporary folder`);
130
- }
131
120
  return module.name;
132
121
  }
133
122
 
134
123
  // Collects all module prefixes from module hierarchy into 'this.modules'.
135
124
  // Note that collected result can contain duplicates.
136
- private async collectModulePrefixes(modules: ModuleSetting[]) {
125
+ private async collectModulePrefixes(
126
+ modules: ModuleSetting[],
127
+ credentials?: Credentials,
128
+ ) {
137
129
  if (modules) {
138
130
  for (const module of modules) {
139
- await this.doCollectModulePrefix(module);
131
+ await this.doCollectModulePrefix(module, credentials);
140
132
  }
141
133
  }
142
134
  }
@@ -153,21 +145,24 @@ export class ModuleManager {
153
145
 
154
146
  // Collects one module's dependency prefixes to 'this.modules'.
155
147
  // Note that there can be duplicate entries.
156
- private async doCollectModulePrefix(module: ModuleSetting) {
148
+ private async doCollectModulePrefix(
149
+ module: ModuleSetting,
150
+ credentials?: Credentials,
151
+ ) {
157
152
  let moduleRoot = '';
158
153
  if (this.isFileModule(module)) {
159
154
  const urlStart = FILE_PROTOCOL.length;
160
155
  // Remove 'file:' from location
161
156
  moduleRoot = module.location.substring(urlStart, module.location.length);
162
157
  } else {
163
- await this.clone(module, false);
158
+ await this.clone(module, false, credentials);
164
159
  moduleRoot = join(this.tempModulesDir, module.name);
165
160
  }
166
161
 
167
162
  this.modules.push(module);
168
163
 
169
164
  const configuration = await this.configuration(moduleRoot);
170
- await this.collectModulePrefixes(configuration.modules);
165
+ await this.collectModulePrefixes(configuration.modules, credentials);
171
166
  }
172
167
 
173
168
  // Updates one module that is read from local file system.
@@ -180,21 +175,20 @@ export class ModuleManager {
180
175
  // Updates one module that is received from Git.
181
176
  private async handleGitModule(module: ModuleSetting) {
182
177
  await this.clone(module);
183
- await this.branch(module);
184
178
  await this.remove(module);
185
179
  await this.importFromTemp(module);
186
180
  }
187
181
 
188
182
  // Updates one module.
189
183
  private async handleModule(module: ModuleSetting) {
190
- return this.isFileModule(module)
191
- ? this.handleFileModule(module)
192
- : this.handleGitModule(module);
184
+ return this.isGitModule(module)
185
+ ? this.handleGitModule(module)
186
+ : this.handleFileModule(module);
193
187
  }
194
188
 
195
189
  // Handles importing a module from module settings 'location'
196
190
  private async importFromFolder(module: ModuleSetting) {
197
- await this.importCmd.updateExistingModule(module.location);
191
+ await this.importFileModule(module.location);
198
192
  console.log(
199
193
  `... Imported module '${module.name}' to '${this.project.configuration.name}'`,
200
194
  );
@@ -202,9 +196,7 @@ export class ModuleManager {
202
196
 
203
197
  // Handles importing a module from '.temp' folder
204
198
  private async importFromTemp(module: ModuleSetting) {
205
- await this.importCmd.updateExistingModule(
206
- join(this.tempModulesDir, module.name),
207
- );
199
+ await this.importFileModule(join(this.tempModulesDir, module.name));
208
200
  console.log(
209
201
  `... Imported module '${module.name}' to '${this.project.configuration.name}'`,
210
202
  );
@@ -216,14 +208,26 @@ export class ModuleManager {
216
208
  return module.location.startsWith('file:');
217
209
  }
218
210
 
211
+ // Returns true if module is imported from git.
212
+ private isGitModule(module: ModuleSetting): boolean {
213
+ if (!module.location) return false;
214
+ return module.location.startsWith('https:');
215
+ }
216
+
219
217
  // Prepares '.temp/modules' for cloning
220
218
  private async prepare() {
221
- await mkdir(this.tempModulesDir, { recursive: true });
222
- for (const file of await readdir(this.tempModulesDir)) {
223
- await rm(join(this.tempModulesDir, file), {
224
- force: true,
225
- recursive: true,
226
- });
219
+ try {
220
+ await mkdir(this.tempModulesDir, { recursive: true });
221
+ const files = await readdir(this.tempModulesDir);
222
+ for (const file of files) {
223
+ const filePath = join(this.tempModulesDir, file);
224
+ await rm(filePath, {
225
+ force: true,
226
+ recursive: true,
227
+ });
228
+ }
229
+ } catch (error) {
230
+ throw new Error(`Failed to prepare temporary directory: ${error}`);
227
231
  }
228
232
  }
229
233
 
@@ -281,11 +285,14 @@ export class ModuleManager {
281
285
  for (const module of modules) {
282
286
  const existingModule = moduleMap.get(module.name);
283
287
  if (existingModule) {
284
- if (existingModule.private !== module.private) {
288
+ if (
289
+ (existingModule.private && !module.private) ||
290
+ (!existingModule.private && module.private)
291
+ ) {
285
292
  throw new Error(
286
293
  `Module conflict: '${module.name}' has different access:\n` +
287
- ` - ${existingModule.private || 'undefined'}\n` +
288
- ` - ${module.private || 'undefined'}`,
294
+ ` - ${existingModule.private === true ? 'true' : 'false'}\n` +
295
+ ` - ${module.private === true ? 'true' : 'false'}`,
289
296
  );
290
297
  }
291
298
  if (existingModule.location !== module.location) {
@@ -319,6 +326,66 @@ export class ModuleManager {
319
326
  return repoName;
320
327
  }
321
328
 
329
+ // Sets cloning options.
330
+ private setCloneOptions(module: ModuleSetting) {
331
+ const cloneOptions = ['--depth', '1'];
332
+ if (
333
+ module.branch &&
334
+ module.branch !== '' &&
335
+ module.branch !== MAIN_BRANCH
336
+ ) {
337
+ cloneOptions.push('--branch', module.branch);
338
+ }
339
+ return cloneOptions;
340
+ }
341
+
342
+ // Updates modules in the project.
343
+ private async update(module?: ModuleSetting, credentials?: Credentials) {
344
+ // Prints dots every half second so that user knows that something is ongoing
345
+ function start() {
346
+ console.log('... Collecting unique modules. This takes a moment.');
347
+ return setInterval(() => process.stdout.write(`.`), 500);
348
+ }
349
+
350
+ // Stops the above, and shows results
351
+ function finished(interval: NodeJS.Timeout, modules: string[]) {
352
+ clearInterval(interval);
353
+ console.log(`\n... Found modules: ${modules.join(', ')}`);
354
+ }
355
+
356
+ await this.prepare();
357
+
358
+ let modules = module ? [module] : this.project.configuration.modules;
359
+ if (modules.length === 0) {
360
+ throw new Error(`No modules in the project!`);
361
+ }
362
+ modules = modules.filter((module) => this.isGitModule(module));
363
+
364
+ const dotInterval = start();
365
+
366
+ let uniqueModules: ModuleSetting[] = [];
367
+ try {
368
+ await this.collectModulePrefixes(modules, credentials);
369
+
370
+ uniqueModules = this.removeDuplicates(this.modules);
371
+ } finally {
372
+ finished(
373
+ dotInterval,
374
+ uniqueModules.map((item) => item.name),
375
+ );
376
+
377
+ // Update modules parallel.
378
+ const promises: Promise<void>[] = [];
379
+ uniqueModules.forEach((module) =>
380
+ promises.push(this.handleModule(module)),
381
+ );
382
+ await Promise.all(promises);
383
+
384
+ await deleteDir(this.tempModulesDir);
385
+ await this.project.collectModuleResources();
386
+ }
387
+ }
388
+
322
389
  // Checks that module prefix is not in use in the project
323
390
  private async validatePrefix(modulePrefix: string) {
324
391
  // Do not allow modules with same prefixes.
@@ -370,16 +437,24 @@ export class ModuleManager {
370
437
  * Imports module from gitUrl.
371
438
  * @param source Git URL to import from.
372
439
  * @param options Modules setting options.
440
+ * @param credentials Credentials for private repositories.
373
441
  * @returns module prefix as defined in its CardsConfig.json
374
442
  */
375
- public async importGitModule(source: string, options?: ModuleSettingOptions) {
376
- const repoName = await this.clone({
377
- name: '',
378
- location: source,
379
- ...options,
380
- });
381
- await this.branch({ name: repoName, location: source, ...options });
382
- const clonePath = join(this.project.paths.tempFolder, 'modules', repoName);
443
+ public async importGitModule(
444
+ source: string,
445
+ options?: ModuleSettingOptions,
446
+ credentials?: Credentials,
447
+ ) {
448
+ const clonedName = await this.clone(
449
+ {
450
+ name: this.repositoryName(source),
451
+ location: source,
452
+ ...options,
453
+ },
454
+ undefined,
455
+ credentials,
456
+ );
457
+ const clonePath = join(this.tempModulesDir, clonedName);
383
458
  const modulePrefix = (await this.configuration(clonePath)).cardKeyPrefix;
384
459
  await this.validatePrefix(modulePrefix);
385
460
 
@@ -393,51 +468,20 @@ export class ModuleManager {
393
468
  }
394
469
 
395
470
  /**
396
- * Updates all imported modules.
471
+ * Imports module from a local file path or a git URL.
472
+ * @param module Module to update. If not provided, updates all modules.
473
+ * @param credentials Optional credentials for private repositories.
474
+ * @returns Module prefix as defined in its CardsConfig.json
397
475
  */
398
- public async update() {
399
- // Prints dots every half second so that user knows that something is ongoing
400
- function start() {
401
- console.log('... Collecting unique modules. This takes a moment.');
402
- return setInterval(() => process.stdout.write(`.`), 500);
403
- }
404
-
405
- // Stops the above, and shows results
406
- function finished(interval: NodeJS.Timeout, modules: string[]) {
407
- clearInterval(interval);
408
- console.log(`\n... Found modules: ${modules.join(', ')}`);
409
- }
410
-
411
- await this.prepare();
412
-
413
- const modules = this.project.configuration.modules;
414
- if (modules.length === 0) {
415
- throw new Error(`No modules in the project!`);
416
- }
417
-
418
- const dotInterval = start();
419
-
420
- // Collect prefixes from project's dependency modules.
421
- await this.collectModulePrefixes(modules);
422
-
423
- let uniqueModules: ModuleSetting[] = [];
424
- try {
425
- uniqueModules = this.removeDuplicates(this.modules);
426
- } finally {
427
- finished(
428
- dotInterval,
429
- uniqueModules.map((item) => item.name),
430
- );
431
-
432
- // Update modules parallel.
433
- const promises: Promise<void>[] = [];
434
- uniqueModules.forEach((module) =>
435
- promises.push(this.handleModule(module)),
436
- );
437
- await Promise.all(promises);
476
+ public async updateModule(module: ModuleSetting, credentials?: Credentials) {
477
+ return this.update(module, credentials);
478
+ }
438
479
 
439
- await deleteDir(this.tempModulesDir);
440
- await this.project.collectModuleResources();
441
- }
480
+ /**
481
+ * Updates all imported modules.
482
+ * @param credentials Optional credentials for private modules.
483
+ */
484
+ public async updateModules(credentials?: Credentials) {
485
+ return this.update(undefined, credentials);
442
486
  }
443
487
  }
@@ -13,6 +13,8 @@
13
13
 
14
14
  import { writeJsonFile as atomicWrite } from 'write-json-file';
15
15
 
16
+ import { resolve } from 'path';
17
+
16
18
  import type {
17
19
  ModuleSetting,
18
20
  ProjectSettings,
@@ -103,6 +105,11 @@ export class ProjectConfiguration implements ProjectSettings {
103
105
  if (exists) {
104
106
  throw new Error(`Module '${module.name}' already imported`);
105
107
  }
108
+ // Ensure that module file location is absolute
109
+ if (module.location && module.location.startsWith('file:')) {
110
+ const filePath = module.location.substring(5, module.location.length);
111
+ module.location = `file:${resolve(filePath)}`;
112
+ }
106
113
  this.modules.push(module);
107
114
  return this.save();
108
115
  }
@@ -83,15 +83,20 @@ export class ArrayHandler<T> {
83
83
  if (targetIndex === -1) {
84
84
  throw new Error(`Item '${JSON.stringify(target)}' not found`);
85
85
  }
86
+ const actualTarget = array[targetIndex];
86
87
  const parsedTo = this.tryParseJSON(to);
87
88
 
88
89
  if (typeof to === 'string' && (to.startsWith('[') || to.startsWith('{'))) {
89
90
  return parsedTo as T[];
90
91
  }
91
92
 
92
- return array.map((item) =>
93
- deepCompare(item as object, target as object) ? parsedTo : item,
93
+ const updatedArray = array.map((item) =>
94
+ deepCompare(item as object, actualTarget as object) ? parsedTo : item,
94
95
  );
96
+ if (deepCompare(updatedArray, array)) {
97
+ throw new Error('Cannot change value. Target was not found.');
98
+ }
99
+ return updatedArray;
95
100
  }
96
101
 
97
102
  private handleRank(operation: RankOperation<T>, array: T[]): T[] {