submodule-version 2.4.1 → 3.1.0
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 +117 -39
- package/dist/runner/handleErrors/handleErrors.d.ts +1 -1
- package/dist/runner/handleErrors/handleErrors.js +3 -10
- package/dist/runner/handleErrors/handleErrors.js.map +1 -1
- package/dist/runner/index.js +0 -0
- package/dist/runner/runner.d.ts +4 -0
- package/dist/runner/runner.js +91 -37
- package/dist/runner/runner.js.map +1 -1
- package/dist/src/PubGrub/PubGrub.d.ts +28 -0
- package/dist/src/PubGrub/PubGrub.js +366 -0
- package/dist/src/PubGrub/PubGrub.js.map +1 -0
- package/dist/src/PubGrub/index.d.ts +2 -0
- package/dist/src/PubGrub/index.js +7 -0
- package/dist/src/PubGrub/index.js.map +1 -0
- package/dist/src/PubGrub/types.d.ts +56 -0
- package/dist/src/PubGrub/types.js +3 -0
- package/dist/src/PubGrub/types.js.map +1 -0
- package/dist/src/entryStore.d.ts +16 -0
- package/dist/src/entryStore.js +152 -0
- package/dist/src/entryStore.js.map +1 -0
- package/dist/src/errors.d.ts +29 -0
- package/dist/src/errors.js +31 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/git/api/GithubError.d.ts +4 -0
- package/dist/src/git/api/GithubError.js +11 -0
- package/dist/src/git/api/GithubError.js.map +1 -0
- package/dist/src/git/api/api.d.ts +8 -0
- package/dist/src/git/api/api.js +133 -0
- package/dist/src/git/api/api.js.map +1 -0
- package/dist/src/git/api/index.d.ts +3 -0
- package/dist/src/git/api/index.js +8 -0
- package/dist/src/git/api/index.js.map +1 -0
- package/dist/src/git/git.d.ts +30 -23
- package/dist/src/git/git.js +8 -258
- package/dist/src/git/git.js.map +1 -1
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +3 -1
- package/dist/src/git/index.js.map +1 -1
- package/dist/src/git/local/index.d.ts +1 -0
- package/dist/src/git/local/index.js +6 -0
- package/dist/src/git/local/index.js.map +1 -0
- package/dist/src/git/local/local.d.ts +26 -0
- package/dist/src/git/local/local.js +288 -0
- package/dist/src/git/local/local.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +16 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/npm.d.ts +3 -0
- package/dist/src/npm.js +25 -0
- package/dist/src/npm.js.map +1 -0
- package/dist/src/printError.d.ts +1 -0
- package/dist/src/printError.js +162 -0
- package/dist/src/printError.js.map +1 -0
- package/dist/src/sv.d.ts +16 -8
- package/dist/src/sv.js +242 -142
- package/dist/src/sv.js.map +1 -1
- package/dist/src/versionUtil/index.d.ts +2 -0
- package/dist/src/versionUtil/index.js +6 -0
- package/dist/src/versionUtil/index.js.map +1 -0
- package/dist/src/versionUtil/versionUtil.d.ts +16 -0
- package/dist/src/versionUtil/versionUtil.js +70 -0
- package/dist/src/versionUtil/versionUtil.js.map +1 -0
- package/package.json +4 -2
- package/runner/handleErrors/handleErrors.ts +3 -10
- package/runner/runner.ts +128 -46
- package/src/PubGrub/PubGrub.ts +578 -0
- package/src/PubGrub/__tests__/PubGrub.test.ts +351 -0
- package/src/PubGrub/index.ts +12 -0
- package/src/PubGrub/types.ts +72 -0
- package/src/__tests__/entryStore.test.ts +248 -0
- package/src/__tests__/printError.test.ts +148 -0
- package/src/__tests__/sv.test.ts +497 -0
- package/src/entryStore.ts +212 -0
- package/src/errors.ts +40 -0
- package/src/git/api/GithubError.ts +5 -0
- package/src/git/api/api.ts +180 -0
- package/src/git/api/index.ts +3 -0
- package/src/git/git.ts +8 -396
- package/src/git/index.ts +1 -0
- package/src/git/local/index.ts +1 -0
- package/src/git/local/local.ts +430 -0
- package/src/index.ts +17 -0
- package/src/npm.ts +25 -0
- package/src/printError.ts +193 -0
- package/src/sv.ts +327 -145
- package/src/versionUtil/__tests__/versionUtil.test.ts +62 -0
- package/src/versionUtil/index.ts +2 -0
- package/src/versionUtil/versionUtil.ts +103 -0
- package/tsconfig.json +1 -1
- package/dist/runner/handleErrors/handleBuildGraphError.d.ts +0 -1
- package/dist/runner/handleErrors/handleBuildGraphError.js +0 -42
- package/dist/runner/handleErrors/handleBuildGraphError.js.map +0 -1
- package/dist/runner/handleErrors/handleGitError.d.ts +0 -1
- package/dist/runner/handleErrors/handleGitError.js +0 -27
- package/dist/runner/handleErrors/handleGitError.js.map +0 -1
- package/dist/runner/handleErrors/handleInstallError.d.ts +0 -1
- package/dist/runner/handleErrors/handleInstallError.js +0 -30
- package/dist/runner/handleErrors/handleInstallError.js.map +0 -1
- package/dist/src/__tests__/versionUtil.test.d.ts +0 -1
- package/dist/src/__tests__/versionUtil.test.js +0 -80
- package/dist/src/__tests__/versionUtil.test.js.map +0 -1
- package/dist/src/buildGraph/GraphError.d.ts +0 -11
- package/dist/src/buildGraph/GraphError.js +0 -11
- package/dist/src/buildGraph/GraphError.js.map +0 -1
- package/dist/src/buildGraph/buildGraph.d.ts +0 -8
- package/dist/src/buildGraph/buildGraph.js +0 -100
- package/dist/src/buildGraph/buildGraph.js.map +0 -1
- package/dist/src/buildGraph/index.d.ts +0 -2
- package/dist/src/buildGraph/index.js +0 -8
- package/dist/src/buildGraph/index.js.map +0 -1
- package/dist/src/git/__tests__/parseGitUrl.test.d.ts +0 -1
- package/dist/src/git/__tests__/parseGitUrl.test.js +0 -30
- package/dist/src/git/__tests__/parseGitUrl.test.js.map +0 -1
- package/dist/src/versionUtil.d.ts +0 -13
- package/dist/src/versionUtil.js +0 -79
- package/dist/src/versionUtil.js.map +0 -1
- package/runner/handleErrors/handleBuildGraphError.ts +0 -49
- package/runner/handleErrors/handleGitError.ts +0 -28
- package/runner/handleErrors/handleInstallError.ts +0 -26
- package/src/__tests__/versionUtil.test.ts +0 -96
- package/src/buildGraph/GraphError.ts +0 -12
- package/src/buildGraph/buildGraph.ts +0 -150
- package/src/buildGraph/index.ts +0 -2
- package/src/versionUtil.ts +0 -97
package/src/sv.ts
CHANGED
|
@@ -1,153 +1,357 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import { pkgJSONManager } from './pkgJSONManager';
|
|
4
|
+
import {
|
|
5
|
+
PubGrub,
|
|
6
|
+
IOverride,
|
|
7
|
+
TDependencies,
|
|
8
|
+
TPubGrubResult,
|
|
9
|
+
} from './PubGrub';
|
|
10
|
+
import { EntryStore } from './entryStore';
|
|
11
|
+
import { RunOptions } from './runOptions';
|
|
12
|
+
import { versionUtil } from './versionUtil';
|
|
13
|
+
import { git, GitError } from './git';
|
|
14
|
+
import { npm } from './npm';
|
|
15
|
+
import { SVError } from './errors';
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Правка package.json родителя (внутри его сабмодуля): parent-level
|
|
19
|
+
* мутации обязаны пережить публикацию родителя, поэтому диапазон
|
|
20
|
+
* пишется в его поле sv.
|
|
21
|
+
*/
|
|
22
|
+
const updateModuleJson = async (
|
|
23
|
+
parent: string,
|
|
24
|
+
override: IOverride,
|
|
25
|
+
): Promise<void> => {
|
|
26
|
+
const parentJson = await pkgJSONManager.read(parent);
|
|
27
|
+
|
|
28
|
+
if (!parentJson) throw new SVError('ADDON_NOT_FOUND', { name: parent });
|
|
29
|
+
|
|
30
|
+
const sv = { ...((parentJson.sv || {}) as TDependencies) };
|
|
31
|
+
|
|
32
|
+
if (override.add) sv[override.add] = override.version || '*';
|
|
33
|
+
|
|
34
|
+
if (override.delete) {
|
|
35
|
+
const key = Object.keys(sv)
|
|
36
|
+
.find(depUrl => git.parseUrl(depUrl).name === override.delete);
|
|
37
|
+
|
|
38
|
+
if (!key) throw new SVError('ADDON_NOT_FOUND', { name: override.delete });
|
|
39
|
+
|
|
40
|
+
delete sv[key];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
parentJson.sv = sv;
|
|
44
|
+
await pkgJSONManager.write(parentJson, parent);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* modulesDir должен быть npm-workspace, иначе сабмодули не попадут
|
|
49
|
+
* в node_modules. Поддерживаем оба формата: workspaces: [] и
|
|
50
|
+
* workspaces: { packages: [] }. Возвращает true, если json изменён.
|
|
51
|
+
*/
|
|
52
|
+
const ensureWorkspaces = (baseJson: any): boolean => {
|
|
53
|
+
const entry = `${RunOptions.modulesDir}/*`;
|
|
54
|
+
|
|
55
|
+
if (Array.isArray(baseJson.workspaces)) {
|
|
56
|
+
if (baseJson.workspaces.includes(entry)) return false;
|
|
57
|
+
|
|
58
|
+
baseJson.workspaces.push(entry);
|
|
59
|
+
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const packages = baseJson.workspaces?.packages;
|
|
64
|
+
|
|
65
|
+
if (Array.isArray(packages)) {
|
|
66
|
+
if (packages.includes(entry)) return false;
|
|
67
|
+
|
|
68
|
+
packages.push(entry);
|
|
69
|
+
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
baseJson.workspaces = [entry];
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const MODULES_DIR_FIELD = 'sv-dir';
|
|
79
|
+
const DEFAULT_MODULES_DIR = 'modules';
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* Уже добавленные сабмодули — источник правды о директории:
|
|
83
|
+
* .gitmodules с единым родителем у всех path (addons/X, addons/Y → addons).
|
|
84
|
+
*/
|
|
85
|
+
const deriveModulesDir = async (): Promise<string | null> => {
|
|
86
|
+
try {
|
|
87
|
+
const content = await readFile(
|
|
88
|
+
path.join(RunOptions.cwd, '.gitmodules'),
|
|
89
|
+
'utf-8',
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const dirs = new Set(
|
|
93
|
+
[...content.matchAll(/^\s*path\s*=\s*(.+)$/gm)]
|
|
94
|
+
.map(match => match[1].trim().split('/')[0]),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return dirs.size === 1 ? [...dirs][0] : null;
|
|
98
|
+
} catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
7
102
|
|
|
8
103
|
export class SV {
|
|
104
|
+
private resolution: TPubGrubResult | null = null;
|
|
105
|
+
|
|
106
|
+
private store = new EntryStore();
|
|
107
|
+
|
|
108
|
+
private explicitModulesDir?: string;
|
|
109
|
+
|
|
9
110
|
constructor (
|
|
10
111
|
projectDir: string,
|
|
11
|
-
modulesDir
|
|
112
|
+
modulesDir?: string,
|
|
113
|
+
options: { githubToken?: string } = {},
|
|
12
114
|
) {
|
|
13
115
|
RunOptions.cwd = projectDir;
|
|
14
|
-
RunOptions.modulesDir = modulesDir;
|
|
116
|
+
RunOptions.modulesDir = modulesDir || DEFAULT_MODULES_DIR;
|
|
117
|
+
this.explicitModulesDir = modulesDir;
|
|
118
|
+
git.api.setToken(options.githubToken);
|
|
15
119
|
}
|
|
16
120
|
|
|
17
|
-
|
|
121
|
+
/*
|
|
122
|
+
* Директория модулей: явно переданная (CLI/JS) → записанная в
|
|
123
|
+
* package.json#sv-dir → выведенная из .gitmodules → 'modules'.
|
|
124
|
+
* Эффективная записывается в sv-dir. Возвращает true, если json изменён.
|
|
125
|
+
*/
|
|
126
|
+
private syncModulesDir = async (baseJson: any): Promise<boolean> => {
|
|
127
|
+
const recorded = baseJson[MODULES_DIR_FIELD] as string | undefined;
|
|
18
128
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
129
|
+
if (!this.explicitModulesDir) {
|
|
130
|
+
if (recorded) {
|
|
131
|
+
RunOptions.modulesDir = recorded;
|
|
22
132
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
26
135
|
|
|
27
|
-
|
|
28
|
-
const installedPkg = await pkgJSONManager.read(name);
|
|
136
|
+
const derived = await deriveModulesDir();
|
|
29
137
|
|
|
30
|
-
|
|
31
|
-
await git.addSumbmodule(url);
|
|
138
|
+
if (derived) RunOptions.modulesDir = derived;
|
|
32
139
|
}
|
|
33
140
|
|
|
34
|
-
|
|
35
|
-
const latestVersion = versionUtil.latest(versions);
|
|
36
|
-
const requestedVersion = version || latestVersion;
|
|
141
|
+
if (recorded === RunOptions.modulesDir) return false;
|
|
37
142
|
|
|
38
|
-
|
|
39
|
-
throw new Error('REQUESTED_VERSION_NOT_EXISTS');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!targetPkg.sv) {
|
|
43
|
-
targetPkg.sv = {};
|
|
44
|
-
}
|
|
143
|
+
baseJson[MODULES_DIR_FIELD] = RunOptions.modulesDir;
|
|
45
144
|
|
|
46
|
-
|
|
47
|
-
await pkgJSONManager.write(targetPkg, parentModule);
|
|
48
|
-
await buildGraph();
|
|
145
|
+
return true;
|
|
49
146
|
};
|
|
50
147
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
148
|
+
private resolveDeps = (
|
|
149
|
+
rootDeps: TDependencies,
|
|
150
|
+
): Promise<TPubGrubResult> => new PubGrub(this.store).resolve(rootDeps);
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* Синк рабочей копии с резолвом: недостающие сабмодули добавляются,
|
|
154
|
+
* версии переключаются, исчезнувшие из резолва модули удаляются.
|
|
155
|
+
* Возвращает true, если рабочая копия изменилась.
|
|
156
|
+
*/
|
|
157
|
+
private syncModules = async (
|
|
158
|
+
resolution: TPubGrubResult,
|
|
159
|
+
): Promise<boolean> => {
|
|
160
|
+
const previous = this.resolution || {};
|
|
161
|
+
let changed = false;
|
|
162
|
+
|
|
163
|
+
await Promise.all(Object.entries(resolution).map(async ([name, entry]) => {
|
|
164
|
+
const dir = path.join(RunOptions.modulesDir, name);
|
|
165
|
+
|
|
166
|
+
if (!await git.local.isGitRepo(dir)) {
|
|
167
|
+
await git.local.addSubmodule(entry.url);
|
|
168
|
+
changed = true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (await git.local.currentVersion(name) !== entry.version) {
|
|
172
|
+
await git.local.checkout(name, entry.version);
|
|
173
|
+
changed = true;
|
|
60
174
|
}
|
|
61
175
|
}));
|
|
176
|
+
|
|
177
|
+
const removed = Object.keys(previous).filter(name => !resolution[name]);
|
|
178
|
+
|
|
179
|
+
if (removed.length) changed = true;
|
|
180
|
+
|
|
181
|
+
await Promise.all(removed.map(name => git.local.rm(name)));
|
|
182
|
+
|
|
183
|
+
return changed;
|
|
62
184
|
};
|
|
63
185
|
|
|
64
186
|
/*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* последней имеет смысл передать `^<version>`, чтобы новые версии продолжали приходить.
|
|
187
|
+
* Общий путь мутаций: проверка через resolve внутри simulate, и только
|
|
188
|
+
* если ошибки нет — установка/переключение модулей и правка package.json
|
|
189
|
+
* (корня или родителя).
|
|
69
190
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
191
|
+
private apply = async (
|
|
192
|
+
baseJson: any,
|
|
193
|
+
override: IOverride,
|
|
194
|
+
parent?: string,
|
|
195
|
+
): Promise<void> => {
|
|
196
|
+
const dirChanged = await this.syncModulesDir(baseJson);
|
|
197
|
+
const rootDeps = (baseJson.sv || {}) as TDependencies;
|
|
198
|
+
let nextRootDeps = rootDeps;
|
|
199
|
+
|
|
200
|
+
const resolution = await this.store.simulate(
|
|
201
|
+
[override],
|
|
202
|
+
rootDeps,
|
|
203
|
+
deps => {
|
|
204
|
+
nextRootDeps = deps;
|
|
205
|
+
|
|
206
|
+
return this.resolveDeps(deps);
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
await this.syncModules(resolution);
|
|
211
|
+
|
|
212
|
+
if (parent) {
|
|
213
|
+
await updateModuleJson(parent, override);
|
|
214
|
+
|
|
215
|
+
if (dirChanged) await pkgJSONManager.write(baseJson);
|
|
216
|
+
} else {
|
|
217
|
+
baseJson.sv = nextRootDeps;
|
|
218
|
+
await pkgJSONManager.write(baseJson);
|
|
76
219
|
}
|
|
77
220
|
|
|
78
|
-
const dir = path.join(RunOptions.modulesDir, name);
|
|
79
|
-
const dirty = await git.hasChanges(dir);
|
|
80
|
-
|
|
81
221
|
/*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* Значит при ошибке мы гарантированно остаёмся на исходной версии с теми
|
|
85
|
-
* же изменениями — сообщаем, что перенос нужно сделать вручную.
|
|
222
|
+
* package.json уже записан — npm подтянет workspaces и зависимости
|
|
223
|
+
* нового набора модулей.
|
|
86
224
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
225
|
+
await npm.install();
|
|
226
|
+
|
|
227
|
+
this.resolution = resolution;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
public init = async (): Promise<void> => {
|
|
231
|
+
const baseJson = await pkgJSONManager.read();
|
|
232
|
+
|
|
233
|
+
if (!baseJson) throw new SVError('NOT_A_NPM');
|
|
93
234
|
|
|
94
|
-
|
|
235
|
+
if (!await git.local.isGitRepo(RunOptions.cwd)) {
|
|
236
|
+
throw new SVError('NOT_A_GIT_REPO');
|
|
95
237
|
}
|
|
96
238
|
|
|
97
|
-
const
|
|
98
|
-
|
|
239
|
+
const dirChanged = await this.syncModulesDir(baseJson);
|
|
240
|
+
const jsonChanged = ensureWorkspaces(baseJson) || dirChanged;
|
|
99
241
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (key && json.sv[key] !== nextConstraint) {
|
|
103
|
-
json.sv[key] = nextConstraint;
|
|
104
|
-
await pkgJSONManager.write(json);
|
|
242
|
+
if (jsonChanged) {
|
|
243
|
+
await pkgJSONManager.write(baseJson);
|
|
105
244
|
}
|
|
245
|
+
|
|
246
|
+
const rootDeps = (baseJson.sv || {}) as TDependencies;
|
|
247
|
+
const resolution = await this.resolveDeps(rootDeps);
|
|
248
|
+
const modulesChanged = await this.syncModules(resolution);
|
|
249
|
+
|
|
250
|
+
/*
|
|
251
|
+
* npm install запускается только когда реально что-то изменилось —
|
|
252
|
+
* иначе каждый CLI-вызов (init идёт перед любой командой) платил
|
|
253
|
+
* бы за полный прогон npm.
|
|
254
|
+
*/
|
|
255
|
+
if (jsonChanged || modulesChanged) await npm.install();
|
|
256
|
+
|
|
257
|
+
this.resolution = resolution;
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
public getResolution = (): TPubGrubResult => {
|
|
261
|
+
if (!this.resolution) throw new SVError('NOT_INITIALIZED');
|
|
262
|
+
|
|
263
|
+
return this.resolution;
|
|
106
264
|
};
|
|
107
265
|
|
|
108
266
|
/*
|
|
109
|
-
*
|
|
267
|
+
* Установка/смена версии модуля. Первый аргумент — url репозитория
|
|
268
|
+
* или имя уже установленного модуля. Без parentName — в корне проекта,
|
|
269
|
+
* без version — максимально возможная ('*'). Второй аргумент —
|
|
270
|
+
* версия, если это валидный semver-диапазон, иначе имя родителя.
|
|
110
271
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
272
|
+
public put = async (
|
|
273
|
+
url: string,
|
|
274
|
+
versionOrParent?: string,
|
|
275
|
+
parentName?: string,
|
|
276
|
+
): Promise<void> => {
|
|
277
|
+
const isVersion = !!versionOrParent
|
|
278
|
+
&& versionUtil.validate(versionOrParent, true);
|
|
115
279
|
|
|
116
|
-
const
|
|
280
|
+
const range = (isVersion ? versionOrParent : undefined) || '*';
|
|
281
|
+
const parent = (isVersion ? parentName : versionOrParent) || undefined;
|
|
282
|
+
const baseJson = await pkgJSONManager.read();
|
|
117
283
|
|
|
118
|
-
|
|
119
|
-
};
|
|
284
|
+
if (!baseJson) throw new SVError('NOT_A_NPM');
|
|
120
285
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
286
|
+
/*
|
|
287
|
+
* Вместо url можно передать имя уже установленного модуля —
|
|
288
|
+
* url берём из текущего резолва.
|
|
289
|
+
*/
|
|
290
|
+
let moduleUrl = url;
|
|
291
|
+
|
|
292
|
+
if (!git.parseUrl(url).name) {
|
|
293
|
+
const existing = this.getResolution()[url];
|
|
125
294
|
|
|
126
|
-
|
|
295
|
+
if (!existing) throw new SVError('NOT_A_GIT_URL', { url });
|
|
127
296
|
|
|
128
|
-
|
|
129
|
-
json.sv[key] = constraint;
|
|
130
|
-
await pkgJSONManager.write(json);
|
|
297
|
+
moduleUrl = existing.url;
|
|
131
298
|
}
|
|
299
|
+
|
|
300
|
+
if (parent && !this.getResolution()[parent]) {
|
|
301
|
+
throw new SVError('ADDON_NOT_FOUND', { name: parent });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
await this.apply(
|
|
305
|
+
baseJson,
|
|
306
|
+
{ parent, add: moduleUrl, version: range },
|
|
307
|
+
parent,
|
|
308
|
+
);
|
|
132
309
|
};
|
|
133
310
|
|
|
134
|
-
|
|
135
|
-
public
|
|
136
|
-
await
|
|
137
|
-
|
|
311
|
+
/* Удаление модуля из родителя (без parentName — из корня проекта). */
|
|
312
|
+
public delete = async (name: string, parentName?: string): Promise<void> => {
|
|
313
|
+
const baseJson = await pkgJSONManager.read();
|
|
314
|
+
|
|
315
|
+
if (!baseJson) throw new SVError('NOT_A_NPM');
|
|
138
316
|
|
|
139
|
-
if (
|
|
317
|
+
if (parentName) {
|
|
318
|
+
const parentEntry = this.getResolution()[parentName];
|
|
140
319
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return acc;
|
|
320
|
+
if (!parentEntry) {
|
|
321
|
+
throw new SVError('ADDON_NOT_FOUND', { name: parentName });
|
|
144
322
|
}
|
|
145
323
|
|
|
146
|
-
|
|
147
|
-
|
|
324
|
+
if (!(name in parentEntry.dependencies)) {
|
|
325
|
+
throw new SVError('ADDON_NOT_FOUND', { name });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
await this.apply(
|
|
330
|
+
baseJson,
|
|
331
|
+
{ parent: parentName, delete: name },
|
|
332
|
+
parentName,
|
|
333
|
+
);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
public listVersions = (
|
|
337
|
+
name?: string,
|
|
338
|
+
): Record<string, string[]> | string[] => {
|
|
339
|
+
const resolution = this.getResolution();
|
|
340
|
+
|
|
341
|
+
if (name) {
|
|
342
|
+
const entry = resolution[name];
|
|
343
|
+
|
|
344
|
+
if (!entry) throw new SVError('ADDON_NOT_FOUND', { name });
|
|
345
|
+
|
|
346
|
+
return versionUtil.sort(Object.keys(entry.versions));
|
|
347
|
+
}
|
|
148
348
|
|
|
149
|
-
|
|
150
|
-
|
|
349
|
+
return Object.fromEntries(
|
|
350
|
+
Object.entries(resolution).map(([entryName, entry]) => [
|
|
351
|
+
entryName,
|
|
352
|
+
versionUtil.sort(Object.keys(entry.versions)),
|
|
353
|
+
]),
|
|
354
|
+
);
|
|
151
355
|
};
|
|
152
356
|
|
|
153
357
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -156,11 +360,9 @@ export class SV {
|
|
|
156
360
|
? path.join(RunOptions.modulesDir, module)
|
|
157
361
|
: RunOptions.cwd;
|
|
158
362
|
|
|
159
|
-
|
|
160
|
-
* нет ли незапушенных коммитов (идёт fetch в remote) */
|
|
161
|
-
if (await git.hasChanges(dir)) return true;
|
|
363
|
+
if (await git.local.hasChanges(dir)) return true;
|
|
162
364
|
|
|
163
|
-
return git.hasUnpushedCommits(dir);
|
|
365
|
+
return git.local.hasUnpushedCommits(dir);
|
|
164
366
|
};
|
|
165
367
|
|
|
166
368
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -169,7 +371,7 @@ export class SV {
|
|
|
169
371
|
? path.join(RunOptions.modulesDir, module)
|
|
170
372
|
: RunOptions.cwd;
|
|
171
373
|
|
|
172
|
-
return git.hasChanges(dir);
|
|
374
|
+
return git.local.hasChanges(dir);
|
|
173
375
|
};
|
|
174
376
|
|
|
175
377
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -178,7 +380,7 @@ export class SV {
|
|
|
178
380
|
? path.join(RunOptions.modulesDir, module)
|
|
179
381
|
: RunOptions.cwd;
|
|
180
382
|
|
|
181
|
-
const tags = await git.tagsAtHead(dir);
|
|
383
|
+
const tags = await git.local.tagsAtHead(dir);
|
|
182
384
|
|
|
183
385
|
return versionUtil.latest(tags) || null;
|
|
184
386
|
};
|
|
@@ -189,7 +391,7 @@ export class SV {
|
|
|
189
391
|
? path.join(RunOptions.modulesDir, module)
|
|
190
392
|
: RunOptions.cwd;
|
|
191
393
|
|
|
192
|
-
const versions = await git.listTags(dir);
|
|
394
|
+
const versions = await git.local.listTags(dir);
|
|
193
395
|
|
|
194
396
|
return versionUtil.latest(versions) || '0.0.0';
|
|
195
397
|
};
|
|
@@ -200,7 +402,7 @@ export class SV {
|
|
|
200
402
|
? path.join(RunOptions.modulesDir, module)
|
|
201
403
|
: RunOptions.cwd;
|
|
202
404
|
|
|
203
|
-
return git.getRemote(dir);
|
|
405
|
+
return git.local.getRemote(dir);
|
|
204
406
|
};
|
|
205
407
|
|
|
206
408
|
public isPublished = async (module?: string): Promise<boolean> => {
|
|
@@ -208,38 +410,29 @@ export class SV {
|
|
|
208
410
|
? path.join(RunOptions.modulesDir, module)
|
|
209
411
|
: RunOptions.cwd;
|
|
210
412
|
|
|
211
|
-
if (!await git.isGitRepo(dir)) return false;
|
|
413
|
+
if (!await git.local.isGitRepo(dir)) return false;
|
|
212
414
|
|
|
213
415
|
return !!(await this.getRemote(module));
|
|
214
416
|
};
|
|
215
417
|
|
|
216
|
-
/*
|
|
217
|
-
* Remote-ветка впереди локальной (fetch + rev-list HEAD..@{u}).
|
|
218
|
-
* Нет репозитория/remote/upstream — обновлений нет.
|
|
219
|
-
*/
|
|
220
418
|
// eslint-disable-next-line class-methods-use-this
|
|
221
419
|
public isRemoteAhead = async (module?: string): Promise<boolean> => {
|
|
222
420
|
const dir = module
|
|
223
421
|
? path.join(RunOptions.modulesDir, module)
|
|
224
422
|
: RunOptions.cwd;
|
|
225
423
|
|
|
226
|
-
if (!await git.isGitRepo(dir)) return false;
|
|
424
|
+
if (!await git.local.isGitRepo(dir)) return false;
|
|
227
425
|
|
|
228
|
-
return git.isRemoteAhead(dir);
|
|
426
|
+
return git.local.isRemoteAhead(dir);
|
|
229
427
|
};
|
|
230
428
|
|
|
231
|
-
/*
|
|
232
|
-
* Догоняем remote: pull --rebase --autostash. При конфликте всё
|
|
233
|
-
* восстанавливается как было (rebase --abort + stash pop) и кидается
|
|
234
|
-
* GIT_SYNC_CONFLICT — дальше возможен только ручной ребейз.
|
|
235
|
-
*/
|
|
236
429
|
// eslint-disable-next-line class-methods-use-this
|
|
237
430
|
public pullRebaseAutostash = async (module?: string): Promise<void> => {
|
|
238
431
|
const dir = module
|
|
239
432
|
? path.join(RunOptions.modulesDir, module)
|
|
240
433
|
: RunOptions.cwd;
|
|
241
434
|
|
|
242
|
-
return git.pullRebaseAutostash(dir);
|
|
435
|
+
return git.local.pullRebaseAutostash(dir);
|
|
243
436
|
};
|
|
244
437
|
|
|
245
438
|
public publish = async (opts: {
|
|
@@ -249,24 +442,19 @@ export class SV {
|
|
|
249
442
|
bump: 'release' | 'minor' | 'major',
|
|
250
443
|
}): Promise<string> => {
|
|
251
444
|
const { module, repoUrl, message, bump } = opts;
|
|
445
|
+
|
|
252
446
|
const dir = module
|
|
253
447
|
? path.join(RunOptions.modulesDir, module)
|
|
254
448
|
: RunOptions.cwd;
|
|
255
449
|
|
|
256
450
|
if (!await this.isPublished(module)) {
|
|
257
|
-
if (!repoUrl)
|
|
258
|
-
throw new GitError('GIT_REPO_URL_REQUIRED');
|
|
259
|
-
}
|
|
451
|
+
if (!repoUrl) throw new GitError('GIT_REPO_URL_REQUIRED');
|
|
260
452
|
|
|
261
|
-
if (!await git.isGitRepo(dir))
|
|
262
|
-
await git.init(dir);
|
|
263
|
-
}
|
|
453
|
+
if (!await git.local.isGitRepo(dir)) await git.local.init(dir);
|
|
264
454
|
|
|
265
|
-
await git.addRemote(dir, repoUrl);
|
|
455
|
+
await git.local.addRemote(dir, repoUrl);
|
|
266
456
|
}
|
|
267
457
|
|
|
268
|
-
/* Публикация не последней версии запрещена: HEAD на теге, отличном
|
|
269
|
-
* от последнего, — обновлять такую базу можно только вручную */
|
|
270
458
|
const currentTag = await this.currentVersion(module);
|
|
271
459
|
const latestKnown = await this.latestVersion(module);
|
|
272
460
|
|
|
@@ -274,41 +462,35 @@ export class SV {
|
|
|
274
462
|
throw new GitError('GIT_NOT_LATEST_VERSION');
|
|
275
463
|
}
|
|
276
464
|
|
|
277
|
-
|
|
278
|
-
* иначе коммиты и push уйдут в detached HEAD */
|
|
279
|
-
await git.ensureBranch(dir);
|
|
465
|
+
await git.local.ensureBranch(dir);
|
|
280
466
|
|
|
281
|
-
if (await git.isRemoteAhead(dir)) {
|
|
282
|
-
await git.pullRebaseAutostash(dir);
|
|
467
|
+
if (await git.local.isRemoteAhead(dir)) {
|
|
468
|
+
await git.local.pullRebaseAutostash(dir);
|
|
283
469
|
}
|
|
284
470
|
|
|
285
|
-
const dirty = await git.hasChanges(dir);
|
|
471
|
+
const dirty = await git.local.hasChanges(dir);
|
|
286
472
|
const headTag = await this.currentVersion(module);
|
|
287
473
|
|
|
288
|
-
/* Дерево чистое и HEAD уже отмечен версионным тегом:
|
|
289
|
-
* коммитить и бампить нечего — просто доставляем ветку и тег на remote */
|
|
290
474
|
if (!dirty && headTag) {
|
|
291
|
-
await git.push(dir, headTag);
|
|
475
|
+
await git.local.push(dir, headTag);
|
|
292
476
|
|
|
293
477
|
return headTag;
|
|
294
478
|
}
|
|
295
479
|
|
|
296
480
|
const next = versionUtil.bump(await this.latestVersion(module), bump);
|
|
297
|
-
|
|
298
|
-
/* Фиксируем версию и в package.json, чтобы она не расходилась с git-тегом */
|
|
299
481
|
const pkg = await pkgJSONManager.read(module);
|
|
482
|
+
|
|
300
483
|
if (pkg) {
|
|
301
484
|
pkg.version = next;
|
|
302
485
|
await pkgJSONManager.write(pkg, module);
|
|
303
486
|
}
|
|
304
487
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
await git.commitAll(dir, message || 'Update');
|
|
488
|
+
if (await git.local.hasChanges(dir)) {
|
|
489
|
+
await git.local.commitAll(dir, message || 'Update');
|
|
308
490
|
}
|
|
309
491
|
|
|
310
|
-
await git.addTag(dir, next);
|
|
311
|
-
await git.push(dir, next);
|
|
492
|
+
await git.local.addTag(dir, next);
|
|
493
|
+
await git.local.push(dir, next);
|
|
312
494
|
|
|
313
495
|
return next;
|
|
314
496
|
};
|