@cyberismo/data-handler 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/card-metadata-updater.d.ts +1 -0
- package/dist/card-metadata-updater.js +7 -2
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.d.ts +6 -1
- package/dist/command-handler.js +16 -15
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +15 -4
- package/dist/command-manager.js +41 -9
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/calculate.d.ts +4 -10
- package/dist/commands/calculate.js +67 -78
- package/dist/commands/calculate.js.map +1 -1
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +15 -10
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/export.js +3 -3
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.d.ts +3 -8
- package/dist/commands/import.js +17 -15
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/index.d.ts +1 -2
- package/dist/commands/index.js +1 -2
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -1
- package/dist/commands/remove.js +4 -10
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/show.d.ts +6 -3
- package/dist/commands/show.js +8 -5
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.d.ts +0 -8
- package/dist/commands/validate.js +6 -36
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/project/project-content-watcher.d.ts +28 -0
- package/dist/containers/project/project-content-watcher.js +54 -0
- package/dist/containers/project/project-content-watcher.js.map +1 -0
- package/dist/containers/project/project-paths.js +1 -1
- package/dist/containers/project/project-paths.js.map +1 -1
- package/dist/containers/project/resource-collector.d.ts +2 -1
- package/dist/containers/project/resource-collector.js +33 -23
- package/dist/containers/project/resource-collector.js.map +1 -1
- package/dist/containers/project.d.ts +9 -7
- package/dist/containers/project.js +58 -18
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.d.ts +1 -0
- package/dist/containers/template.js +7 -2
- package/dist/containers/template.js.map +1 -1
- package/dist/exceptions/index.d.ts +20 -0
- package/dist/exceptions/index.js +16 -0
- package/dist/exceptions/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/macros.d.ts +5 -1
- package/dist/interfaces/project-interfaces.d.ts +5 -0
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/interfaces/resource-interfaces.d.ts +14 -22
- package/dist/interfaces/resource-interfaces.js +10 -9
- package/dist/interfaces/resource-interfaces.js.map +1 -1
- package/dist/macros/base-macro.d.ts +2 -0
- package/dist/macros/base-macro.js +66 -19
- package/dist/macros/base-macro.js.map +1 -1
- package/dist/macros/graph/index.d.ts +0 -1
- package/dist/macros/graph/index.js +16 -12
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/index.d.ts +30 -3
- package/dist/macros/index.js +36 -6
- package/dist/macros/index.js.map +1 -1
- package/dist/macros/report/index.d.ts +13 -10
- package/dist/macros/report/index.js +35 -38
- package/dist/macros/report/index.js.map +1 -1
- package/dist/module-manager.d.ts +32 -11
- package/dist/module-manager.js +301 -147
- package/dist/module-manager.js.map +1 -1
- package/dist/project-settings.js +8 -8
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/array-handler.js +6 -1
- package/dist/resources/array-handler.js.map +1 -1
- package/dist/resources/card-type-resource.d.ts +13 -9
- package/dist/resources/card-type-resource.js +47 -23
- package/dist/resources/card-type-resource.js.map +1 -1
- package/dist/resources/create-defaults.d.ts +10 -9
- package/dist/resources/create-defaults.js +15 -12
- package/dist/resources/create-defaults.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +0 -1
- package/dist/resources/field-type-resource.js +2 -10
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/file-resource.d.ts +7 -7
- package/dist/resources/file-resource.js +32 -7
- package/dist/resources/file-resource.js.map +1 -1
- package/dist/resources/folder-resource.d.ts +10 -9
- package/dist/resources/folder-resource.js +10 -9
- package/dist/resources/folder-resource.js.map +1 -1
- package/dist/resources/report-resource.d.ts +5 -6
- package/dist/resources/report-resource.js +16 -7
- package/dist/resources/report-resource.js.map +1 -1
- package/dist/resources/template-resource.d.ts +5 -6
- package/dist/resources/template-resource.js +7 -6
- package/dist/resources/template-resource.js.map +1 -1
- package/dist/resources/workflow-resource.d.ts +15 -8
- package/dist/resources/workflow-resource.js +124 -8
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/types/queries.d.ts +11 -10
- package/dist/types/queries.js +10 -9
- package/dist/types/queries.js.map +1 -1
- package/dist/utils/clingo-fact-builder.d.ts +1 -0
- package/dist/utils/clingo-fact-builder.js +8 -3
- package/dist/utils/clingo-fact-builder.js.map +1 -1
- package/dist/utils/clingo-facts.js +15 -11
- package/dist/utils/clingo-facts.js.map +1 -1
- package/dist/utils/constants.d.ts +18 -12
- package/dist/utils/constants.js +18 -11
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/log-utils.d.ts +15 -2
- package/dist/utils/log-utils.js +20 -37
- package/dist/utils/log-utils.js.map +1 -1
- package/dist/utils/report.d.ts +27 -0
- package/dist/utils/report.js +60 -0
- package/dist/utils/report.js.map +1 -0
- package/dist/utils/resource-utils.js +3 -0
- package/dist/utils/resource-utils.js.map +1 -1
- package/dist/utils/sanitize-svg.d.ts +3 -4
- package/dist/utils/sanitize-svg.js +4 -7
- package/dist/utils/sanitize-svg.js.map +1 -1
- package/dist/utils/validate.js +2 -1
- package/dist/utils/validate.js.map +1 -1
- package/package.json +9 -11
- package/src/card-metadata-updater.ts +7 -2
- package/src/command-handler.ts +23 -13
- package/src/command-manager.ts +54 -13
- package/src/commands/calculate.ts +90 -106
- package/src/commands/create.ts +18 -10
- package/src/commands/export.ts +3 -2
- package/src/commands/import.ts +30 -17
- package/src/commands/index.ts +0 -2
- package/src/commands/remove.ts +7 -12
- package/src/commands/show.ts +13 -5
- package/src/commands/validate.ts +14 -44
- package/src/containers/project/project-content-watcher.ts +65 -0
- package/src/containers/project/project-paths.ts +1 -1
- package/src/containers/project/resource-collector.ts +33 -14
- package/src/containers/project.ts +96 -19
- package/src/containers/template.ts +7 -2
- package/src/exceptions/index.ts +36 -0
- package/src/index.ts +2 -0
- package/src/interfaces/macros.ts +5 -1
- package/src/interfaces/project-interfaces.ts +8 -0
- package/src/interfaces/resource-interfaces.ts +15 -22
- package/src/macros/base-macro.ts +89 -25
- package/src/macros/graph/index.ts +22 -12
- package/src/macros/index.ts +61 -8
- package/src/macros/report/index.ts +42 -43
- package/src/module-manager.ts +383 -177
- package/src/project-settings.ts +9 -11
- package/src/resources/array-handler.ts +7 -2
- package/src/resources/card-type-resource.ts +61 -46
- package/src/resources/create-defaults.ts +15 -12
- package/src/resources/field-type-resource.ts +2 -17
- package/src/resources/file-resource.ts +46 -8
- package/src/resources/folder-resource.ts +11 -10
- package/src/resources/report-resource.ts +19 -7
- package/src/resources/template-resource.ts +7 -6
- package/src/resources/workflow-resource.ts +155 -8
- package/src/types/queries.ts +11 -10
- package/src/utils/clingo-fact-builder.ts +8 -3
- package/src/utils/clingo-facts.ts +18 -12
- package/src/utils/constants.ts +20 -12
- package/src/utils/log-utils.ts +24 -45
- package/src/utils/report.ts +86 -0
- package/src/utils/resource-utils.ts +4 -0
- package/src/utils/sanitize-svg.ts +4 -9
- package/src/utils/validate.ts +3 -2
- package/dist/commands/export-site.d.ts +0 -45
- package/dist/commands/export-site.js +0 -301
- package/dist/commands/export-site.js.map +0 -1
- package/src/commands/export-site.ts +0 -356
package/src/module-manager.ts
CHANGED
|
@@ -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
|
|
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';
|
|
@@ -31,8 +29,15 @@ import { readJsonFile } from './utils/json.js';
|
|
|
31
29
|
import { Validate } from './commands/index.js';
|
|
32
30
|
|
|
33
31
|
const FILE_PROTOCOL = 'file:';
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const HTTPS_PROTOCOL = 'https:';
|
|
33
|
+
|
|
34
|
+
// timeout in milliseconds for git client (no stdout / stderr activity)
|
|
35
|
+
const DEFAULT_TIMEOUT = 10000;
|
|
36
|
+
|
|
37
|
+
// When dependencies are built to a map, use map that has
|
|
38
|
+
// key: module name,
|
|
39
|
+
// value: list of unique prefixes
|
|
40
|
+
type DependencyGraph = Map<string, Set<string>>;
|
|
36
41
|
|
|
37
42
|
/**
|
|
38
43
|
* Class that handles module updates and imports.
|
|
@@ -40,10 +45,9 @@ const MAIN_BRANCH = 'main';
|
|
|
40
45
|
export class ModuleManager {
|
|
41
46
|
private modules: ModuleSetting[] = [];
|
|
42
47
|
private tempModulesDir: string = '';
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
) {
|
|
48
|
+
private defaultBranchCache: Map<string, string> = new Map();
|
|
49
|
+
|
|
50
|
+
constructor(private project: Project) {
|
|
47
51
|
this.tempModulesDir = join(this.project.paths.tempFolder, 'modules');
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -56,87 +60,121 @@ export class ModuleManager {
|
|
|
56
60
|
await this.project.collectModuleResources();
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
//
|
|
60
|
-
private async
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
// Creates a map of what dependencies each module depend from.
|
|
64
|
+
private async buildDependencyGraph(
|
|
65
|
+
dependencies: ModuleSetting[],
|
|
66
|
+
): Promise<DependencyGraph> {
|
|
67
|
+
const dependencyNames = dependencies.map((item) => item.name);
|
|
68
|
+
const dependencyGraph = new Map<string, Set<string>>() as DependencyGraph;
|
|
69
|
+
for (const dependency of dependencyNames) {
|
|
70
|
+
dependencyGraph.set(
|
|
71
|
+
dependency,
|
|
72
|
+
await this.transientDependencies(dependency),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return dependencyGraph;
|
|
76
|
+
}
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
78
|
+
// Returns 'true' if 'moduleName' can be removed.
|
|
79
|
+
private canBeRemoved(
|
|
80
|
+
dependencies: DependencyGraph,
|
|
81
|
+
moduleName: string,
|
|
82
|
+
): boolean {
|
|
83
|
+
let unused = true;
|
|
84
|
+
dependencies.forEach((transientDependencies, key) => {
|
|
85
|
+
if (key !== moduleName && transientDependencies.has(moduleName)) {
|
|
86
|
+
unused = false;
|
|
87
|
+
}
|
|
68
88
|
});
|
|
69
|
-
|
|
70
|
-
`... Switched to '${module.branch}' branch for module '${module.name}'`,
|
|
71
|
-
);
|
|
89
|
+
return unused;
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
// Handles cloning of a repository.
|
|
75
93
|
private async clone(
|
|
76
94
|
module: ModuleSetting,
|
|
77
95
|
verbose: boolean = true,
|
|
96
|
+
credentials?: Credentials,
|
|
78
97
|
): Promise<string> {
|
|
79
98
|
if (!module.name || module.name === '') {
|
|
80
99
|
module.name = this.repositoryName(module.location);
|
|
81
100
|
}
|
|
82
101
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
const destinationPath = join(this.tempModulesDir, module.name);
|
|
103
|
+
|
|
104
|
+
let remote = module.location;
|
|
105
|
+
if (module.private) {
|
|
106
|
+
if (
|
|
107
|
+
credentials &&
|
|
108
|
+
credentials?.username &&
|
|
109
|
+
credentials?.token &&
|
|
110
|
+
module.location.startsWith(HTTPS_PROTOCOL)
|
|
111
|
+
) {
|
|
112
|
+
if (verbose) {
|
|
113
|
+
console.log(
|
|
114
|
+
`... Using HTTPS with credentials '${credentials?.username}' for cloning '${module.name}'`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const repoUrl = new URL(module.location);
|
|
119
|
+
const user = credentials?.username;
|
|
120
|
+
const pass = credentials?.token;
|
|
121
|
+
const host = repoUrl.host;
|
|
122
|
+
const path = repoUrl.pathname;
|
|
123
|
+
remote = `https://${user}:${pass}@${host}${path}`;
|
|
124
|
+
} catch {
|
|
125
|
+
throw new Error(`Invalid repository URL: ${module.location}`);
|
|
126
|
+
}
|
|
127
|
+
} else if (module.location.startsWith('git@')) {
|
|
128
|
+
if (verbose) {
|
|
129
|
+
console.log(`... Using SSH for cloning '${module.name}'`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
95
133
|
if (verbose) {
|
|
96
134
|
console.log(
|
|
97
|
-
`... Using credentials
|
|
135
|
+
`... Using HTTPS without credentials for cloning '${module.name}'`,
|
|
98
136
|
);
|
|
99
137
|
}
|
|
100
|
-
repoUrl.username = process.env.CYBERISMO_GIT_USER;
|
|
101
|
-
repoUrl.password = process.env.CYBERISMO_GIT_TOKEN;
|
|
102
138
|
}
|
|
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
139
|
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
try {
|
|
141
|
+
await mkdir(this.tempModulesDir, { recursive: true });
|
|
142
|
+
const cloneOptions = await this.setCloneOptions(module);
|
|
143
|
+
await rm(destinationPath, { recursive: true, force: true });
|
|
144
|
+
|
|
145
|
+
const git: SimpleGit = simpleGit({
|
|
146
|
+
timeout: {
|
|
147
|
+
block: DEFAULT_TIMEOUT,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await git
|
|
152
|
+
// turn off git prompts
|
|
153
|
+
.env({ GIT_TERMINAL_PROMPT: 0, GCM_INTERACTIVE: 'never' })
|
|
154
|
+
.clone(remote, destinationPath, cloneOptions);
|
|
155
|
+
|
|
156
|
+
if (verbose) {
|
|
157
|
+
console.log(`... Cloned '${module.name}' to a temporary folder`);
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error instanceof Error)
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Failed to clone module '${module.name}': ${error.message}`,
|
|
163
|
+
);
|
|
130
164
|
}
|
|
165
|
+
|
|
131
166
|
return module.name;
|
|
132
167
|
}
|
|
133
168
|
|
|
134
169
|
// Collects all module prefixes from module hierarchy into 'this.modules'.
|
|
135
170
|
// Note that collected result can contain duplicates.
|
|
136
|
-
private async collectModulePrefixes(
|
|
171
|
+
private async collectModulePrefixes(
|
|
172
|
+
modules: ModuleSetting[],
|
|
173
|
+
credentials?: Credentials,
|
|
174
|
+
) {
|
|
137
175
|
if (modules) {
|
|
138
176
|
for (const module of modules) {
|
|
139
|
-
await this.doCollectModulePrefix(module);
|
|
177
|
+
await this.doCollectModulePrefix(module, credentials);
|
|
140
178
|
}
|
|
141
179
|
}
|
|
142
180
|
}
|
|
@@ -151,62 +189,97 @@ export class ModuleManager {
|
|
|
151
189
|
}
|
|
152
190
|
}
|
|
153
191
|
|
|
192
|
+
// Gets the default branch for a repository from remote or cache
|
|
193
|
+
private async defaultBranch(module: ModuleSetting): Promise<string> {
|
|
194
|
+
if (this.defaultBranchCache.has(module.location)) {
|
|
195
|
+
return this.defaultBranchCache.get(module.location)!;
|
|
196
|
+
}
|
|
197
|
+
// Set the default branch if branch was not specified
|
|
198
|
+
if (!module.branch) {
|
|
199
|
+
const destinationPath = join(this.tempModulesDir, module.name);
|
|
200
|
+
// Only return path after cloning
|
|
201
|
+
if (pathExists(destinationPath)) {
|
|
202
|
+
const git: SimpleGit = simpleGit({
|
|
203
|
+
timeout: {
|
|
204
|
+
block: DEFAULT_TIMEOUT,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
const options = ['--abbrev-ref', 'HEAD'];
|
|
208
|
+
const defaultBranch = await git.cwd(destinationPath).revparse(options);
|
|
209
|
+
this.defaultBranchCache.set(module.location, defaultBranch);
|
|
210
|
+
return defaultBranch;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// The actual default branch will be updated later (after cloning).
|
|
214
|
+
return '';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Fetches direct dependencies of a module.
|
|
218
|
+
private async dependencies(moduleName: string): Promise<Set<string>> {
|
|
219
|
+
const allModules = await this.project.modules();
|
|
220
|
+
if (!allModules) return new Set();
|
|
221
|
+
const module = allModules.find((m) => m.name === moduleName);
|
|
222
|
+
if (!module) {
|
|
223
|
+
throw new Error(`Module '${moduleName}' not found`);
|
|
224
|
+
}
|
|
225
|
+
const modulePath = join(module.path, module.name, 'cardsConfig.json');
|
|
226
|
+
const moduleConfiguration = (await readJsonFile(
|
|
227
|
+
modulePath,
|
|
228
|
+
)) as ProjectConfiguration;
|
|
229
|
+
return moduleConfiguration.modules
|
|
230
|
+
? new Set(moduleConfiguration.modules.map((m) => m.name))
|
|
231
|
+
: new Set();
|
|
232
|
+
}
|
|
233
|
+
|
|
154
234
|
// Collects one module's dependency prefixes to 'this.modules'.
|
|
155
235
|
// Note that there can be duplicate entries.
|
|
156
|
-
private async doCollectModulePrefix(
|
|
236
|
+
private async doCollectModulePrefix(
|
|
237
|
+
module: ModuleSetting,
|
|
238
|
+
credentials?: Credentials,
|
|
239
|
+
) {
|
|
157
240
|
let moduleRoot = '';
|
|
158
241
|
if (this.isFileModule(module)) {
|
|
159
242
|
const urlStart = FILE_PROTOCOL.length;
|
|
160
243
|
// Remove 'file:' from location
|
|
161
244
|
moduleRoot = module.location.substring(urlStart, module.location.length);
|
|
162
245
|
} else {
|
|
163
|
-
await this.clone(module, false);
|
|
246
|
+
await this.clone(module, false, credentials);
|
|
164
247
|
moduleRoot = join(this.tempModulesDir, module.name);
|
|
165
248
|
}
|
|
166
249
|
|
|
167
250
|
this.modules.push(module);
|
|
168
251
|
|
|
169
252
|
const configuration = await this.configuration(moduleRoot);
|
|
170
|
-
await this.collectModulePrefixes(configuration.modules);
|
|
253
|
+
await this.collectModulePrefixes(configuration.modules, credentials);
|
|
171
254
|
}
|
|
172
255
|
|
|
173
256
|
// Updates one module that is read from local file system.
|
|
174
257
|
private async handleFileModule(module: ModuleSetting) {
|
|
175
|
-
this.
|
|
258
|
+
this.stripProtocolFromLocation(module);
|
|
176
259
|
await this.remove(module);
|
|
177
|
-
await this.importFromFolder(module);
|
|
260
|
+
await this.importFromFolder(module.location, module.name);
|
|
178
261
|
}
|
|
179
262
|
|
|
180
263
|
// Updates one module that is received from Git.
|
|
181
264
|
private async handleGitModule(module: ModuleSetting) {
|
|
182
265
|
await this.clone(module);
|
|
183
|
-
|
|
266
|
+
const tempLocation = join(this.tempModulesDir, module.name);
|
|
184
267
|
await this.remove(module);
|
|
185
|
-
await this.
|
|
268
|
+
await this.importFromFolder(tempLocation, module.name);
|
|
186
269
|
}
|
|
187
270
|
|
|
188
271
|
// Updates one module.
|
|
189
272
|
private async handleModule(module: ModuleSetting) {
|
|
190
|
-
return this.
|
|
191
|
-
? this.
|
|
192
|
-
: this.
|
|
273
|
+
return this.isGitModule(module)
|
|
274
|
+
? this.handleGitModule(module)
|
|
275
|
+
: this.handleFileModule(module);
|
|
193
276
|
}
|
|
194
277
|
|
|
195
|
-
//
|
|
196
|
-
private async importFromFolder(
|
|
197
|
-
await this.
|
|
278
|
+
// Imports from a given folder. Is used both for .temp/<module name> and file locations.
|
|
279
|
+
private async importFromFolder(path: string, name: string) {
|
|
280
|
+
await this.importFileModule(path);
|
|
198
281
|
console.log(
|
|
199
|
-
`... Imported module '${
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Handles importing a module from '.temp' folder
|
|
204
|
-
private async importFromTemp(module: ModuleSetting) {
|
|
205
|
-
await this.importCmd.updateExistingModule(
|
|
206
|
-
join(this.tempModulesDir, module.name),
|
|
207
|
-
);
|
|
208
|
-
console.log(
|
|
209
|
-
`... Imported module '${module.name}' to '${this.project.configuration.name}'`,
|
|
282
|
+
`... Imported module '${name}' to '${this.project.configuration.name}'`,
|
|
210
283
|
);
|
|
211
284
|
}
|
|
212
285
|
|
|
@@ -216,20 +289,61 @@ export class ModuleManager {
|
|
|
216
289
|
return module.location.startsWith('file:');
|
|
217
290
|
}
|
|
218
291
|
|
|
219
|
-
//
|
|
220
|
-
private
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
292
|
+
// Returns true if module is imported from git.
|
|
293
|
+
private isGitModule(module: ModuleSetting): boolean {
|
|
294
|
+
if (!module.location) return false;
|
|
295
|
+
return (
|
|
296
|
+
module.location.startsWith('https:') || module.location.startsWith('git@')
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Collect modules that could be removed from .cards/modules when
|
|
301
|
+
// 'moduleName' is removed.
|
|
302
|
+
private async orphanedModules(
|
|
303
|
+
dependencies: DependencyGraph,
|
|
304
|
+
moduleName: string,
|
|
305
|
+
): Promise<string[]> {
|
|
306
|
+
const projectModules = this.project.configuration.modules;
|
|
307
|
+
const removableTransientModules: string[] = [];
|
|
308
|
+
if (dependencies.has(moduleName)) {
|
|
309
|
+
const deps = dependencies.get(moduleName);
|
|
310
|
+
for (const dependency of deps!) {
|
|
311
|
+
const projectDependency = projectModules.some(
|
|
312
|
+
(item) => item.name === dependency,
|
|
313
|
+
);
|
|
314
|
+
if (projectDependency) continue;
|
|
315
|
+
|
|
316
|
+
let orphanModule = true;
|
|
317
|
+
dependencies.forEach((transientDependencies, key) => {
|
|
318
|
+
if (key === moduleName) return;
|
|
319
|
+
if (transientDependencies.has(dependency)) {
|
|
320
|
+
orphanModule = false;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (orphanModule) {
|
|
325
|
+
removableTransientModules.push(dependency);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
227
328
|
}
|
|
329
|
+
return removableTransientModules;
|
|
228
330
|
}
|
|
229
331
|
|
|
230
|
-
//
|
|
231
|
-
private
|
|
232
|
-
|
|
332
|
+
// Prepares '.temp/modules' for cloning
|
|
333
|
+
private async prepare() {
|
|
334
|
+
try {
|
|
335
|
+
await mkdir(this.tempModulesDir, { recursive: true });
|
|
336
|
+
const files = await readdir(this.tempModulesDir);
|
|
337
|
+
for (const file of files) {
|
|
338
|
+
const filePath = join(this.tempModulesDir, file);
|
|
339
|
+
await rm(filePath, {
|
|
340
|
+
force: true,
|
|
341
|
+
recursive: true,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
} catch (error) {
|
|
345
|
+
throw new Error(`Failed to prepare temporary directory: ${error}`);
|
|
346
|
+
}
|
|
233
347
|
}
|
|
234
348
|
|
|
235
349
|
// Handles removing an imported module.
|
|
@@ -245,47 +359,32 @@ export class ModuleManager {
|
|
|
245
359
|
}
|
|
246
360
|
}
|
|
247
361
|
|
|
248
|
-
// Remove module files.
|
|
249
|
-
private async removeModuleFiles(moduleName: string) {
|
|
250
|
-
const module = await this.project.module(moduleName);
|
|
251
|
-
if (!module) {
|
|
252
|
-
throw new Error(`Module '${moduleName}' not found`);
|
|
253
|
-
}
|
|
254
|
-
await deleteDir(module.path);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Updates module's 'location' not to have 'protocol:' in the beginning (only for "file:" needed).
|
|
258
|
-
private removeProtocolFromLocation(module: ModuleSetting) {
|
|
259
|
-
const protocol = this.protocol(module);
|
|
260
|
-
module.location = module.location.substring(
|
|
261
|
-
protocol.length + 1,
|
|
262
|
-
module.location.length,
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
362
|
// Checks for duplicate ModuleSetting entries and throws an error if modules
|
|
267
363
|
// with the same name have different branches or locations.
|
|
268
|
-
// Treats undefined branch, empty string branch, and
|
|
364
|
+
// Treats undefined branch, empty string branch, and default branch as equivalent.
|
|
269
365
|
// Returns an array with duplicate entries removed
|
|
270
|
-
private removeDuplicates(
|
|
366
|
+
private async removeDuplicates(
|
|
367
|
+
modules: ModuleSetting[],
|
|
368
|
+
): Promise<ModuleSetting[]> {
|
|
271
369
|
const moduleMap = new Map<string, ModuleSetting>();
|
|
272
370
|
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
return branch;
|
|
371
|
+
// Normalize branch names by checking against the default branch for each module
|
|
372
|
+
|
|
373
|
+
const normalizeBranch = async (module: ModuleSetting) => {
|
|
374
|
+
return module.branch ? module.branch! : await this.defaultBranch(module);
|
|
279
375
|
};
|
|
280
376
|
|
|
281
377
|
for (const module of modules) {
|
|
282
378
|
const existingModule = moduleMap.get(module.name);
|
|
283
379
|
if (existingModule) {
|
|
284
|
-
if (
|
|
380
|
+
if (
|
|
381
|
+
(existingModule.private && !module.private) ||
|
|
382
|
+
(!existingModule.private && module.private)
|
|
383
|
+
) {
|
|
285
384
|
throw new Error(
|
|
286
385
|
`Module conflict: '${module.name}' has different access:\n` +
|
|
287
|
-
` - ${existingModule.private
|
|
288
|
-
` - ${module.private
|
|
386
|
+
` - ${Boolean(existingModule.private)}\n` +
|
|
387
|
+
` - ${Boolean(module.private)}`,
|
|
289
388
|
);
|
|
290
389
|
}
|
|
291
390
|
if (existingModule.location !== module.location) {
|
|
@@ -295,8 +394,8 @@ export class ModuleManager {
|
|
|
295
394
|
` - ${module.location}`,
|
|
296
395
|
);
|
|
297
396
|
}
|
|
298
|
-
const existingBranch = normalizeBranch(existingModule
|
|
299
|
-
const newBranch = normalizeBranch(module
|
|
397
|
+
const existingBranch = await normalizeBranch(existingModule);
|
|
398
|
+
const newBranch = await normalizeBranch(module);
|
|
300
399
|
|
|
301
400
|
if (existingBranch !== newBranch) {
|
|
302
401
|
throw new Error(
|
|
@@ -312,6 +411,15 @@ export class ModuleManager {
|
|
|
312
411
|
return Array.from(moduleMap.values());
|
|
313
412
|
}
|
|
314
413
|
|
|
414
|
+
// Remove module files.
|
|
415
|
+
private async removeModuleFiles(moduleName: string) {
|
|
416
|
+
const module = await this.project.module(moduleName);
|
|
417
|
+
if (!module) {
|
|
418
|
+
throw new Error(`Module '${moduleName}' not found`);
|
|
419
|
+
}
|
|
420
|
+
await deleteDir(module.path);
|
|
421
|
+
}
|
|
422
|
+
|
|
315
423
|
// Gets repository name from gitUrl
|
|
316
424
|
private repositoryName(gitUrl: string): string {
|
|
317
425
|
const last = gitUrl.lastIndexOf('/');
|
|
@@ -319,6 +427,94 @@ export class ModuleManager {
|
|
|
319
427
|
return repoName;
|
|
320
428
|
}
|
|
321
429
|
|
|
430
|
+
// Sets cloning options with support for default branch.
|
|
431
|
+
private async setCloneOptions(module: ModuleSetting): Promise<string[]> {
|
|
432
|
+
const cloneOptions = ['--depth', '1'];
|
|
433
|
+
const defaultBranch = await this.defaultBranch(module);
|
|
434
|
+
// Only specify branch if it's different from the default branch
|
|
435
|
+
if (
|
|
436
|
+
module.branch &&
|
|
437
|
+
module.branch !== '' &&
|
|
438
|
+
module.branch !== defaultBranch
|
|
439
|
+
) {
|
|
440
|
+
cloneOptions.push('--branch', module.branch);
|
|
441
|
+
}
|
|
442
|
+
return cloneOptions;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Updates module's 'location' not to have 'protocol:' in the beginning (only for "file:" needed).
|
|
446
|
+
private stripProtocolFromLocation(module: ModuleSetting) {
|
|
447
|
+
const protocol = this.isFileModule(module) ? 'file' : 'git';
|
|
448
|
+
module.location = module.location.substring(
|
|
449
|
+
protocol.length + 1,
|
|
450
|
+
module.location.length,
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Fetches all dependencies for a module.
|
|
455
|
+
private async transientDependencies(
|
|
456
|
+
moduleName: string,
|
|
457
|
+
): Promise<Set<string>> {
|
|
458
|
+
const dependencies = await this.dependencies(moduleName);
|
|
459
|
+
let transientDependencies: Set<string> = new Set(dependencies);
|
|
460
|
+
for (const dependency of dependencies) {
|
|
461
|
+
const depTransients = await this.transientDependencies(dependency);
|
|
462
|
+
transientDependencies = new Set([
|
|
463
|
+
...transientDependencies,
|
|
464
|
+
...depTransients,
|
|
465
|
+
]);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return transientDependencies;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Updates modules in the project.
|
|
472
|
+
private async update(module?: ModuleSetting, credentials?: Credentials) {
|
|
473
|
+
// Prints dots every half second so that user knows that something is ongoing
|
|
474
|
+
function start() {
|
|
475
|
+
console.log('... Collecting unique modules. This takes a moment.');
|
|
476
|
+
return setInterval(() => process.stdout.write(`.`), 500);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Stops the above, and shows results
|
|
480
|
+
function finished(interval: NodeJS.Timeout, modules: string[]) {
|
|
481
|
+
clearInterval(interval);
|
|
482
|
+
if (modules.length > 0) {
|
|
483
|
+
console.log(`\n... Found modules: ${modules.join(', ')}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
await this.prepare();
|
|
488
|
+
|
|
489
|
+
let modules = module ? [module] : this.project.configuration.modules;
|
|
490
|
+
if (modules.length === 0) {
|
|
491
|
+
throw new Error(`No modules in the project!`);
|
|
492
|
+
}
|
|
493
|
+
modules = modules.filter((module) => this.isGitModule(module));
|
|
494
|
+
|
|
495
|
+
const dotInterval = start();
|
|
496
|
+
|
|
497
|
+
let uniqueModules: ModuleSetting[] = [];
|
|
498
|
+
try {
|
|
499
|
+
await this.collectModulePrefixes(modules, credentials);
|
|
500
|
+
uniqueModules = await this.removeDuplicates(this.modules);
|
|
501
|
+
} finally {
|
|
502
|
+
finished(
|
|
503
|
+
dotInterval,
|
|
504
|
+
uniqueModules.map((item) => item.name),
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// Update modules parallel.
|
|
508
|
+
const promises: Promise<void>[] = [];
|
|
509
|
+
uniqueModules.forEach((module) =>
|
|
510
|
+
promises.push(this.handleModule(module)),
|
|
511
|
+
);
|
|
512
|
+
await Promise.all(promises);
|
|
513
|
+
await deleteDir(this.tempModulesDir);
|
|
514
|
+
await this.project.collectModuleResources();
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
322
518
|
// Checks that module prefix is not in use in the project
|
|
323
519
|
private async validatePrefix(modulePrefix: string) {
|
|
324
520
|
// Do not allow modules with same prefixes.
|
|
@@ -370,16 +566,24 @@ export class ModuleManager {
|
|
|
370
566
|
* Imports module from gitUrl.
|
|
371
567
|
* @param source Git URL to import from.
|
|
372
568
|
* @param options Modules setting options.
|
|
569
|
+
* @param credentials Credentials for private repositories.
|
|
373
570
|
* @returns module prefix as defined in its CardsConfig.json
|
|
374
571
|
*/
|
|
375
|
-
public async importGitModule(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
572
|
+
public async importGitModule(
|
|
573
|
+
source: string,
|
|
574
|
+
options?: ModuleSettingOptions,
|
|
575
|
+
credentials?: Credentials,
|
|
576
|
+
) {
|
|
577
|
+
const clonedName = await this.clone(
|
|
578
|
+
{
|
|
579
|
+
name: this.repositoryName(source),
|
|
580
|
+
location: source,
|
|
581
|
+
...options,
|
|
582
|
+
},
|
|
583
|
+
undefined,
|
|
584
|
+
credentials,
|
|
585
|
+
);
|
|
586
|
+
const clonePath = join(this.tempModulesDir, clonedName);
|
|
383
587
|
const modulePrefix = (await this.configuration(clonePath)).cardKeyPrefix;
|
|
384
588
|
await this.validatePrefix(modulePrefix);
|
|
385
589
|
|
|
@@ -393,51 +597,53 @@ export class ModuleManager {
|
|
|
393
597
|
}
|
|
394
598
|
|
|
395
599
|
/**
|
|
396
|
-
*
|
|
600
|
+
* Removed module from project.
|
|
601
|
+
* If module is not used by any other modules, then will remove the module from disk as well.
|
|
602
|
+
* Otherwise, only updates project configuration.
|
|
603
|
+
* @param moduleName Name of the module to remove
|
|
397
604
|
*/
|
|
398
|
-
public async
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
return setInterval(() => process.stdout.write(`.`), 500);
|
|
403
|
-
}
|
|
605
|
+
public async removeModule(moduleName: string) {
|
|
606
|
+
const projectModules = this.project.configuration.modules;
|
|
607
|
+
const dependencies = await this.buildDependencyGraph(projectModules);
|
|
608
|
+
const module = await this.project.module(moduleName);
|
|
404
609
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
clearInterval(interval);
|
|
408
|
-
console.log(`\n... Found modules: ${modules.join(', ')}`);
|
|
610
|
+
if (!module) {
|
|
611
|
+
throw new Error(`Module '${moduleName}' not found`);
|
|
409
612
|
}
|
|
410
613
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (
|
|
415
|
-
|
|
614
|
+
// Project module can always be removed from project configuration,
|
|
615
|
+
// but modules under .cards/modules must be checked not to be used by
|
|
616
|
+
// other modules.
|
|
617
|
+
if (this.canBeRemoved(dependencies, moduleName)) {
|
|
618
|
+
const orphans = await this.orphanedModules(dependencies, moduleName);
|
|
619
|
+
await deleteDir(module.path);
|
|
620
|
+
for (const moduleToDelete of orphans) {
|
|
621
|
+
const modulePath = join(
|
|
622
|
+
this.project.paths.modulesFolder,
|
|
623
|
+
moduleToDelete,
|
|
624
|
+
);
|
|
625
|
+
await deleteDir(modulePath);
|
|
626
|
+
}
|
|
627
|
+
await this.project.collectModuleResources();
|
|
416
628
|
}
|
|
629
|
+
await this.project.configuration.removeModule(moduleName);
|
|
630
|
+
}
|
|
417
631
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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);
|
|
632
|
+
/**
|
|
633
|
+
* Imports module from a local file path or a git URL.
|
|
634
|
+
* @param module Module to update. If not provided, updates all modules.
|
|
635
|
+
* @param credentials Optional credentials for private repositories.
|
|
636
|
+
* @returns Module prefix as defined in its CardsConfig.json
|
|
637
|
+
*/
|
|
638
|
+
public async updateModule(module: ModuleSetting, credentials?: Credentials) {
|
|
639
|
+
return this.update(module, credentials);
|
|
640
|
+
}
|
|
438
641
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
642
|
+
/**
|
|
643
|
+
* Updates all imported modules.
|
|
644
|
+
* @param credentials Optional credentials for private modules.
|
|
645
|
+
*/
|
|
646
|
+
public async updateModules(credentials?: Credentials) {
|
|
647
|
+
return this.update(undefined, credentials);
|
|
442
648
|
}
|
|
443
649
|
}
|