reslop 0.1.8 → 0.2.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/CHANGELOG.md +25 -1
- package/README.md +27 -38
- package/bin/reslop.js +2 -2
- package/lib/ansi.js +6 -2
- package/lib/api.js +2 -2
- package/lib/capabilities.js +10 -14
- package/lib/cli.js +28 -47
- package/lib/clipboard.js +1 -1
- package/lib/deps-diff.js +712 -0
- package/lib/deps.js +556 -32
- package/lib/detect.js +1 -7
- package/lib/diff/align.js +7 -10
- package/lib/diff/diff.js +53 -0
- package/lib/diff/display.js +5 -4
- package/lib/diff/inline.js +26 -21
- package/lib/diff/items.js +6 -4
- package/lib/diff/patch.js +1 -3
- package/lib/diff/unit.js +392 -0
- package/lib/files.js +34 -8
- package/lib/{git/branches.js → git-branches.js} +103 -10
- package/lib/{git/dependencies.js → git-deps.js} +111 -38
- package/lib/git.js +592 -65
- package/lib/github.js +28 -197
- package/lib/gitlab.js +24 -195
- package/lib/highlight/bash.js +1 -3
- package/lib/highlight/css.js +1 -3
- package/lib/highlight/js.js +11 -14
- package/lib/highlight.js +6 -9
- package/lib/keys.js +22 -9
- package/lib/manifest.js +330 -0
- package/lib/remote.js +374 -0
- package/lib/render/branches.js +34 -66
- package/lib/render/chrome.js +57 -22
- package/lib/render/commits.js +85 -0
- package/lib/render/diff.js +16 -16
- package/lib/render/files.js +29 -37
- package/lib/render/frame.js +29 -13
- package/lib/render/notes.js +4 -3
- package/lib/render/primitives.js +46 -1
- package/lib/{render.js → render/render.js} +6 -7
- package/lib/render/unit.js +41 -0
- package/lib/review.js +750 -14
- package/lib/select.js +5 -10
- package/lib/session/accessors.js +9 -1
- package/lib/session/actions.js +93 -36
- package/lib/session/branches.js +130 -144
- package/lib/session/change-actions.js +206 -124
- package/lib/session/commit.js +217 -62
- package/lib/session/compose.js +158 -13
- package/lib/session/dispatch.js +109 -55
- package/lib/session/files-pane.js +145 -142
- package/lib/session/items.js +131 -34
- package/lib/session/lifecycle.js +82 -42
- package/lib/session/load.js +19 -33
- package/lib/session/navigation.js +15 -3
- package/lib/session/ops.js +28 -41
- package/lib/session/pointer.js +58 -54
- package/lib/session/review.js +23 -37
- package/lib/session/terminal.js +6 -12
- package/lib/session/todos.js +8 -12
- package/lib/session/unit.js +208 -0
- package/lib/session/update.js +33 -46
- package/lib/session/view.js +82 -11
- package/lib/session/watch.js +11 -19
- package/lib/session.js +29 -226
- package/lib/source.js +3 -3
- package/lib/update.js +40 -75
- package/lib/{git/proc.js → utilities.js} +75 -11
- package/lib/wrap.js +1 -3
- package/package.json +2 -2
- package/lib/deps/changes.js +0 -331
- package/lib/deps/items.js +0 -131
- package/lib/deps/manifest.js +0 -145
- package/lib/deps/merge.js +0 -120
- package/lib/deps/proposals.js +0 -164
- package/lib/deps/reports.js +0 -180
- package/lib/deps/rows.js +0 -177
- package/lib/deps/scan.js +0 -138
- package/lib/deps/usage.js +0 -68
- package/lib/deps/versions.js +0 -119
- package/lib/diff.js +0 -33
- package/lib/git/changes.js +0 -164
- package/lib/git/read.js +0 -253
- package/lib/git/run.js +0 -100
- package/lib/npm/commands.js +0 -100
- package/lib/remote/changes.js +0 -37
- package/lib/remote/headers.js +0 -36
- package/lib/remote/http.js +0 -122
- package/lib/remote/notes.js +0 -94
- package/lib/review/format.js +0 -434
- package/lib/review/storage.js +0 -150
- package/lib/review/store.js +0 -178
- package/lib/review/templates.js +0 -49
- package/lib/session/origins.js +0 -7
- package/lib/sys.js +0 -84
package/lib/highlight.js
CHANGED
|
@@ -8,14 +8,12 @@ const { tokensText } = require('./highlight/core.js');
|
|
|
8
8
|
const PLUGIN_DIR = path.join(__dirname, 'highlight');
|
|
9
9
|
const SKIP = ['core.js'];
|
|
10
10
|
|
|
11
|
-
const isPluginFile = (name) => name.endsWith('.js') && !SKIP.includes(name);
|
|
12
|
-
|
|
13
|
-
const loadPlugin = (file) => require(path.join(PLUGIN_DIR, file));
|
|
14
|
-
|
|
15
11
|
const loadPlugins = () => {
|
|
16
|
-
const names = fs
|
|
12
|
+
const names = fs
|
|
13
|
+
.readdirSync(PLUGIN_DIR)
|
|
14
|
+
.filter((name) => name.endsWith('.js') && !SKIP.includes(name));
|
|
17
15
|
names.sort();
|
|
18
|
-
return names.map(
|
|
16
|
+
return names.map((file) => require(path.join(PLUGIN_DIR, file)));
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
const indexPlugins = (plugins) => {
|
|
@@ -50,9 +48,8 @@ const tokenize = (lang, source) => {
|
|
|
50
48
|
};
|
|
51
49
|
|
|
52
50
|
const overlayTokens = (tokens, spans) => {
|
|
53
|
-
const syn = tokens
|
|
54
|
-
const diff =
|
|
55
|
-
spans && spans.length ? spans : [{ text: tokensText(syn), changed: false }];
|
|
51
|
+
const syn = tokens?.length ? tokens : [{ text: '', style: 'plain' }];
|
|
52
|
+
const diff = spans?.length ? spans : [{ text: tokensText(syn) }];
|
|
56
53
|
const pieces = [];
|
|
57
54
|
let tokenIndex = 0;
|
|
58
55
|
let spanIndex = 0;
|
package/lib/keys.js
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const actions = require('./session/actions.js');
|
|
4
4
|
|
|
5
|
-
const { ACTIONS, ACTION_IDS, BRANCH_ACTIONS } = actions;
|
|
5
|
+
const { ACTIONS, ACTION_IDS, BRANCH_ACTIONS, COMMIT_ACTIONS } = actions;
|
|
6
6
|
const { DIFF_DISABLED, FILES_DISABLED, FILES_GIT_DISABLED } = actions;
|
|
7
|
-
const { FILES_TODO_DISABLED, TODO_DISABLED, BRANCHES_DISABLED } =
|
|
7
|
+
const { FILES_HIDDEN, FILES_TODO_DISABLED, TODO_DISABLED, BRANCHES_DISABLED } =
|
|
8
|
+
actions;
|
|
9
|
+
const { COMMITS_DISABLED, UNIT_DISABLED } = actions;
|
|
8
10
|
const { disabledActions, diffGitDisabled, branchGitDisabled } = actions;
|
|
11
|
+
const { commitGitDisabled } = actions;
|
|
9
12
|
const { actionFromKey, actionHot, actionLetter, buttonWord } = actions;
|
|
10
13
|
const { CONFIRM, CONFIRM_MODE, confirmKey, applyConfirm } = actions;
|
|
11
14
|
const { confirmPrefix, promptFromChoices } = actions;
|
|
@@ -130,38 +133,43 @@ const parseEsc = (text, start) => {
|
|
|
130
133
|
|
|
131
134
|
const decodeChunk = (text, carry = '') => {
|
|
132
135
|
const source = carry + text;
|
|
133
|
-
const events =
|
|
136
|
+
const events = new Array(source.length);
|
|
137
|
+
let n = 0;
|
|
134
138
|
let i = 0;
|
|
135
139
|
while (i < source.length) {
|
|
136
140
|
const ch = source[i];
|
|
137
141
|
if (ch === '\x1b') {
|
|
138
142
|
const parsed = parseEsc(source, i);
|
|
139
|
-
if (parsed.needMore)
|
|
140
|
-
|
|
143
|
+
if (parsed.needMore) {
|
|
144
|
+
events.length = n;
|
|
145
|
+
return { events, carry: source.slice(i) };
|
|
146
|
+
}
|
|
147
|
+
events[n++] = parsed.event;
|
|
141
148
|
i = parsed.end;
|
|
142
149
|
continue;
|
|
143
150
|
}
|
|
144
151
|
const named = LITERAL_KEY[ch];
|
|
145
152
|
if (named) {
|
|
146
|
-
events
|
|
153
|
+
events[n++] = { type: 'key', key: named };
|
|
147
154
|
i += 1;
|
|
148
155
|
continue;
|
|
149
156
|
}
|
|
150
157
|
const code = source.charCodeAt(i);
|
|
151
158
|
if (code >= 1 && code <= 26) {
|
|
152
159
|
const letter = String.fromCharCode(96 + code);
|
|
153
|
-
events
|
|
160
|
+
events[n++] = { type: 'key', key: `ctrl-${letter}` };
|
|
154
161
|
i += 1;
|
|
155
162
|
continue;
|
|
156
163
|
}
|
|
157
164
|
if (code >= 0xd800 && code <= 0xdbff && i + 1 < source.length) {
|
|
158
|
-
events
|
|
165
|
+
events[n++] = { type: 'key', key: source.slice(i, i + 2) };
|
|
159
166
|
i += 2;
|
|
160
167
|
continue;
|
|
161
168
|
}
|
|
162
|
-
events
|
|
169
|
+
events[n++] = { type: 'key', key: ch };
|
|
163
170
|
i += 1;
|
|
164
171
|
}
|
|
172
|
+
events.length = n;
|
|
165
173
|
return { events, carry: '' };
|
|
166
174
|
};
|
|
167
175
|
|
|
@@ -176,15 +184,20 @@ module.exports = {
|
|
|
176
184
|
ACTIONS,
|
|
177
185
|
ACTION_IDS,
|
|
178
186
|
BRANCH_ACTIONS,
|
|
187
|
+
COMMIT_ACTIONS,
|
|
179
188
|
DIFF_DISABLED,
|
|
180
189
|
FILES_DISABLED,
|
|
190
|
+
FILES_HIDDEN,
|
|
181
191
|
FILES_GIT_DISABLED,
|
|
182
192
|
FILES_TODO_DISABLED,
|
|
183
193
|
TODO_DISABLED,
|
|
184
194
|
BRANCHES_DISABLED,
|
|
195
|
+
COMMITS_DISABLED,
|
|
196
|
+
UNIT_DISABLED,
|
|
185
197
|
disabledActions,
|
|
186
198
|
diffGitDisabled,
|
|
187
199
|
branchGitDisabled,
|
|
200
|
+
commitGitDisabled,
|
|
188
201
|
decodeChunk,
|
|
189
202
|
actionFromKey,
|
|
190
203
|
actionHot,
|
package/lib/manifest.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { isHashObject, jsonParse } = require('metautil');
|
|
5
|
+
const { parseVersion, cmpVersion } = require('./utilities.js');
|
|
6
|
+
|
|
7
|
+
const MANIFEST = 'package.json';
|
|
8
|
+
const LOCKFILE = 'package-lock.json';
|
|
9
|
+
const DEP_KIND = { [MANIFEST]: 'manifest', [LOCKFILE]: 'lockfile' };
|
|
10
|
+
const SECTION_KEYS = [
|
|
11
|
+
'dependencies',
|
|
12
|
+
'devDependencies',
|
|
13
|
+
'optionalDependencies',
|
|
14
|
+
'peerDependencies',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const posixRel = (rel) => {
|
|
18
|
+
const raw = `${rel ?? ''}`;
|
|
19
|
+
return raw.replaceAll('\\', '/');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const depFileMeta = (rel) => {
|
|
23
|
+
const normalized = posixRel(rel);
|
|
24
|
+
if (!normalized) return null;
|
|
25
|
+
const base = path.posix.basename(normalized);
|
|
26
|
+
const kind = DEP_KIND[base];
|
|
27
|
+
if (!kind) return null;
|
|
28
|
+
const dir = path.posix.dirname(normalized);
|
|
29
|
+
return { kind, dir, base, rel: normalized };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const relOf = (dir, name) => (dir === '.' ? name : `${dir}/${name}`);
|
|
33
|
+
|
|
34
|
+
const listFilePath = (rel) => {
|
|
35
|
+
const meta = depFileMeta(rel);
|
|
36
|
+
if (meta && meta.kind === 'lockfile') return relOf(meta.dir, MANIFEST);
|
|
37
|
+
return posixRel(rel);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const readSectionMap = (pkg, key) => {
|
|
41
|
+
const map = new Map();
|
|
42
|
+
const raw = pkg ? pkg[key] : null;
|
|
43
|
+
if (!isHashObject(raw)) return map;
|
|
44
|
+
for (const name of Object.keys(raw)) {
|
|
45
|
+
const version = raw[name];
|
|
46
|
+
if (typeof version === 'string') map.set(name, version);
|
|
47
|
+
}
|
|
48
|
+
return map;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const readSections = (pkg) =>
|
|
52
|
+
Object.fromEntries(
|
|
53
|
+
SECTION_KEYS.map((key) => [key, readSectionMap(pkg, key)]),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const emptySectionMaps = () => readSections(null);
|
|
57
|
+
|
|
58
|
+
const lockRootSections = (lock) => {
|
|
59
|
+
if (!lock) return emptySectionMaps();
|
|
60
|
+
const packages = lock.packages;
|
|
61
|
+
const root = packages && typeof packages === 'object' ? packages[''] : null;
|
|
62
|
+
const src = root && typeof root === 'object' ? root : lock;
|
|
63
|
+
return readSections(src);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const lockEntries = (lock) => {
|
|
67
|
+
const versions = new Map();
|
|
68
|
+
if (!lock) return versions;
|
|
69
|
+
const packages = lock.packages;
|
|
70
|
+
if (packages && typeof packages === 'object') {
|
|
71
|
+
for (const key of Object.keys(packages)) {
|
|
72
|
+
const entry = packages[key];
|
|
73
|
+
if (!key.startsWith('node_modules/')) continue;
|
|
74
|
+
if (key.includes('/node_modules/')) continue;
|
|
75
|
+
if (!entry || typeof entry.version !== 'string') continue;
|
|
76
|
+
const name = key.slice('node_modules/'.length);
|
|
77
|
+
versions.set(name, entry.version);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const deps = lock.dependencies;
|
|
81
|
+
if (!deps || typeof deps !== 'object') return versions;
|
|
82
|
+
for (const name of Object.keys(deps)) {
|
|
83
|
+
const entry = deps[name];
|
|
84
|
+
if (versions.has(name)) continue;
|
|
85
|
+
if (!entry || typeof entry.version !== 'string') continue;
|
|
86
|
+
versions.set(name, entry.version);
|
|
87
|
+
}
|
|
88
|
+
return versions;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const lockPackageCount = (lock) => {
|
|
92
|
+
if (!lock) return 0;
|
|
93
|
+
const packages = lock.packages;
|
|
94
|
+
if (packages && typeof packages === 'object') {
|
|
95
|
+
let count = 0;
|
|
96
|
+
for (const key of Object.keys(packages)) {
|
|
97
|
+
if (key !== '') count += 1;
|
|
98
|
+
}
|
|
99
|
+
return count;
|
|
100
|
+
}
|
|
101
|
+
const deps = lock.dependencies;
|
|
102
|
+
if (!deps || typeof deps !== 'object') return 0;
|
|
103
|
+
return Object.keys(deps).length;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const cloneJson = (value) => JSON.parse(JSON.stringify(value ?? null));
|
|
107
|
+
|
|
108
|
+
const detectIndent = (text) => {
|
|
109
|
+
const match = /\n([ \t]+)"/.exec(text);
|
|
110
|
+
if (!match) return 2;
|
|
111
|
+
return match[1];
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const stringifyJson = (value, original) => {
|
|
115
|
+
const indent = detectIndent(original || '');
|
|
116
|
+
const body = JSON.stringify(value, null, indent);
|
|
117
|
+
return `${body}\n`;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const setSectionEntry = (pkg, section, name, version) => {
|
|
121
|
+
if (!version) {
|
|
122
|
+
const map = pkg[section];
|
|
123
|
+
if (!map || typeof map !== 'object') return;
|
|
124
|
+
delete map[name];
|
|
125
|
+
if (!Object.keys(map).length) delete pkg[section];
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const map = pkg[section];
|
|
129
|
+
if (!map || typeof map !== 'object') pkg[section] = {};
|
|
130
|
+
pkg[section][name] = version;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const dropsEntry = (change, side) => {
|
|
134
|
+
if (side === 'new') return change.action === 'removed';
|
|
135
|
+
return change.action === 'added';
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const applyManifestChange = (pkg, change, side) => {
|
|
139
|
+
if (change.section === 'resolved') return pkg;
|
|
140
|
+
const value = side === 'new' ? change.to : change.from;
|
|
141
|
+
if (change.section === 'field') {
|
|
142
|
+
if (value === '') {
|
|
143
|
+
delete pkg[change.name];
|
|
144
|
+
return pkg;
|
|
145
|
+
}
|
|
146
|
+
pkg[change.name] = jsonParse(value ?? '') ?? value;
|
|
147
|
+
return pkg;
|
|
148
|
+
}
|
|
149
|
+
const version = dropsEntry(change, side) ? '' : value;
|
|
150
|
+
setSectionEntry(pkg, change.section, change.name, version);
|
|
151
|
+
return pkg;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const ensureLockRoot = (lock) => {
|
|
155
|
+
if (!lock.packages || typeof lock.packages !== 'object') {
|
|
156
|
+
lock.packages = {};
|
|
157
|
+
}
|
|
158
|
+
const root = lock.packages[''];
|
|
159
|
+
if (!root || typeof root !== 'object') lock.packages[''] = {};
|
|
160
|
+
return lock.packages[''];
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const applyLegacyLockDep = (lock, change, side, src) => {
|
|
164
|
+
const deps = lock.dependencies;
|
|
165
|
+
if (!deps || typeof deps !== 'object') return;
|
|
166
|
+
if (dropsEntry(change, side)) {
|
|
167
|
+
delete deps[change.name];
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const srcDeps = src && src.dependencies;
|
|
171
|
+
if (!srcDeps || typeof srcDeps !== 'object') return;
|
|
172
|
+
const entry = srcDeps[change.name];
|
|
173
|
+
if (entry) deps[change.name] = cloneJson(entry);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const applyLockChange = (lock, change, side, oldLock, newLock) => {
|
|
177
|
+
if (change.section === 'field') return lock;
|
|
178
|
+
const src = side === 'new' ? newLock : oldLock;
|
|
179
|
+
if (change.section !== 'resolved') {
|
|
180
|
+
const root = ensureLockRoot(lock);
|
|
181
|
+
applyManifestChange(root, change, side);
|
|
182
|
+
applyLegacyLockDep(lock, change, side, src);
|
|
183
|
+
}
|
|
184
|
+
const packages = lock.packages;
|
|
185
|
+
if (!packages || typeof packages !== 'object') return lock;
|
|
186
|
+
const key = `node_modules/${change.name}`;
|
|
187
|
+
if (dropsEntry(change, side)) {
|
|
188
|
+
delete packages[key];
|
|
189
|
+
return lock;
|
|
190
|
+
}
|
|
191
|
+
const srcPkgs = src && src.packages;
|
|
192
|
+
if (!srcPkgs || typeof srcPkgs !== 'object') return lock;
|
|
193
|
+
const entry = srcPkgs[key];
|
|
194
|
+
if (entry) packages[key] = cloneJson(entry);
|
|
195
|
+
return lock;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const seedManifest = (oldObj, newObj) => {
|
|
199
|
+
const base = cloneJson(oldObj) || {};
|
|
200
|
+
if (!newObj) return base;
|
|
201
|
+
for (const key of Object.keys(newObj)) {
|
|
202
|
+
const value = newObj[key];
|
|
203
|
+
if (SECTION_KEYS.includes(key)) continue;
|
|
204
|
+
if (Object.hasOwn(base, key)) continue;
|
|
205
|
+
base[key] = cloneJson(value);
|
|
206
|
+
}
|
|
207
|
+
return base;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const mergeDepFile = (baseText, oldText, newText, change, side, kind) => {
|
|
211
|
+
const oldObj = jsonParse(oldText ?? '');
|
|
212
|
+
const newObj = jsonParse(newText ?? '');
|
|
213
|
+
const parsedBase = jsonParse(baseText ?? '');
|
|
214
|
+
let base;
|
|
215
|
+
if (parsedBase) base = cloneJson(parsedBase);
|
|
216
|
+
else if (kind === 'manifest') base = seedManifest(oldObj, newObj);
|
|
217
|
+
else base = cloneJson(oldObj) || {};
|
|
218
|
+
if (kind === 'lockfile') applyLockChange(base, change, side, oldObj, newObj);
|
|
219
|
+
else applyManifestChange(base, change, side);
|
|
220
|
+
const original = baseText || newText || oldText || '';
|
|
221
|
+
return stringifyJson(base, original);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const applyWantedRange = (declared, wanted) => {
|
|
225
|
+
if (!wanted) return declared ?? '';
|
|
226
|
+
if (!declared) return wanted;
|
|
227
|
+
if (/^(file:|git[+@:]|workspace:|https?:|npm:)/.test(declared)) {
|
|
228
|
+
return declared;
|
|
229
|
+
}
|
|
230
|
+
if (declared.includes(' ') || declared.includes('|')) return declared;
|
|
231
|
+
if (declared.includes('<') || declared.includes('>')) return declared;
|
|
232
|
+
const prefix = declared.startsWith('^') || declared.startsWith('~');
|
|
233
|
+
const mark = prefix ? declared[0] : '';
|
|
234
|
+
return `${mark}${wanted}`;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const findDeclared = (pkg, name, type) => {
|
|
238
|
+
if (type && SECTION_KEYS.includes(type)) {
|
|
239
|
+
const map = pkg[type];
|
|
240
|
+
if (map && typeof map[name] === 'string') {
|
|
241
|
+
return { section: type, version: map[name] };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const section of SECTION_KEYS) {
|
|
245
|
+
const map = pkg[section];
|
|
246
|
+
if (map && typeof map[name] === 'string') {
|
|
247
|
+
return { section, version: map[name] };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const fmtVer = (parts) => `${parts.major}.${parts.minor}.${parts.patch}`;
|
|
254
|
+
|
|
255
|
+
const clauseUpper = (clause) => {
|
|
256
|
+
const pattern = /(<=?)\s*(\d+\.\d+\.\d+)/g;
|
|
257
|
+
const matches = clause.matchAll(pattern);
|
|
258
|
+
let best = null;
|
|
259
|
+
for (const match of matches) {
|
|
260
|
+
const op = match[1];
|
|
261
|
+
const parsed = parseVersion(match[2]);
|
|
262
|
+
if (!parsed) continue;
|
|
263
|
+
if (best && cmpVersion(parsed, best.ver) >= 0) continue;
|
|
264
|
+
best = { op, ver: parsed };
|
|
265
|
+
}
|
|
266
|
+
return best;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const patchedFromRange = (current, range) => {
|
|
270
|
+
if (!current || !range) return '';
|
|
271
|
+
const now = parseVersion(current);
|
|
272
|
+
if (!now) return '';
|
|
273
|
+
const clauses = `${range}`.split('||');
|
|
274
|
+
for (const clause of clauses) {
|
|
275
|
+
const upper = clauseUpper(clause);
|
|
276
|
+
if (!upper) continue;
|
|
277
|
+
if (now.major !== upper.ver.major) continue;
|
|
278
|
+
if (cmpVersion(now, upper.ver) > 0) continue;
|
|
279
|
+
if (upper.op === '<') return fmtVer(upper.ver);
|
|
280
|
+
return fmtVer({ ...upper.ver, patch: upper.ver.patch + 1 });
|
|
281
|
+
}
|
|
282
|
+
return '';
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const sameMajor = (from, to) => {
|
|
286
|
+
const left = parseVersion(from);
|
|
287
|
+
const right = parseVersion(to);
|
|
288
|
+
if (!left || !right) return false;
|
|
289
|
+
return left.major === right.major;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const proposedLockVersion = (current, vul) => {
|
|
293
|
+
if (!current) return '';
|
|
294
|
+
if (!vul) return current;
|
|
295
|
+
const fix = vul.fix;
|
|
296
|
+
if (fix && sameMajor(current, fix) && fix !== current) return fix;
|
|
297
|
+
const patched = patchedFromRange(current, vul.range);
|
|
298
|
+
if (patched && patched !== current) return patched;
|
|
299
|
+
return current;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const proposedWanted = (out, vul) => {
|
|
303
|
+
if (out && out.wanted && out.wanted !== out.current) return out.wanted;
|
|
304
|
+
if (vul && vul.fix) return vul.fix;
|
|
305
|
+
return '';
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
module.exports = {
|
|
309
|
+
MANIFEST,
|
|
310
|
+
LOCKFILE,
|
|
311
|
+
DEP_KIND,
|
|
312
|
+
SECTION_KEYS,
|
|
313
|
+
posixRel,
|
|
314
|
+
depFileMeta,
|
|
315
|
+
relOf,
|
|
316
|
+
listFilePath,
|
|
317
|
+
emptySectionMaps,
|
|
318
|
+
readSections,
|
|
319
|
+
lockRootSections,
|
|
320
|
+
lockEntries,
|
|
321
|
+
lockPackageCount,
|
|
322
|
+
cloneJson,
|
|
323
|
+
stringifyJson,
|
|
324
|
+
mergeDepFile,
|
|
325
|
+
applyWantedRange,
|
|
326
|
+
findDeclared,
|
|
327
|
+
patchedFromRange,
|
|
328
|
+
proposedLockVersion,
|
|
329
|
+
proposedWanted,
|
|
330
|
+
};
|