@konce-pt/ui 0.6.0 → 0.6.1
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/dist/commands/add.js +43 -28
- package/dist/semver.js +73 -0
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
3
3
|
import { requireConfig, writeConfig } from "../config.js";
|
|
4
4
|
import { loadIndex, loadBlockMeta, loadBlockFile } from "../registry.js";
|
|
5
5
|
import { flag, hasFlag, log, UserError } from "../util.js";
|
|
6
|
+
import { satisfies } from "../semver.js";
|
|
6
7
|
export async function addCommand(args) {
|
|
7
8
|
const requested = args.positionals;
|
|
8
9
|
if (requested.length === 0)
|
|
@@ -113,6 +114,14 @@ function rewriteImports(content, config) {
|
|
|
113
114
|
}
|
|
114
115
|
return out;
|
|
115
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Weryfikuje zależności bloku względem package.json projektu.
|
|
119
|
+
*
|
|
120
|
+
* Rozróżnienie surowości jest celowe: `npm` to nasze paczki i zła wersja realnie
|
|
121
|
+
* psuje blok, więc przerywamy. `peer` to Angular i rxjs — ich wersję trzyma
|
|
122
|
+
* aplikacja, a twardy błąd na prerelease, którego prosty parser może odczytać
|
|
123
|
+
* inaczej niż npm, byłby gorszy od ostrzeżenia.
|
|
124
|
+
*/
|
|
116
125
|
function checkNpmDependencies(metas, projectRoot) {
|
|
117
126
|
const pkgPath = path.join(projectRoot, 'package.json');
|
|
118
127
|
const installed = {};
|
|
@@ -120,40 +129,46 @@ function checkNpmDependencies(metas, projectRoot) {
|
|
|
120
129
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
121
130
|
Object.assign(installed, pkg.dependencies ?? {}, pkg.devDependencies ?? {});
|
|
122
131
|
}
|
|
123
|
-
const missing = new
|
|
132
|
+
const missing = new Map();
|
|
133
|
+
// Set, bo kilka bloków deklaruje zwykle ten sam peer (@angular/core) — bez tego
|
|
134
|
+
// to samo ostrzeżenie poszłoby raz na blok.
|
|
135
|
+
const peerNotes = new Set();
|
|
124
136
|
for (const meta of metas.values()) {
|
|
125
|
-
for (const [
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
for (const [kind, deps] of [
|
|
138
|
+
['npm', meta.dependencies?.npm ?? {}],
|
|
139
|
+
['peer', meta.dependencies?.peer ?? {}],
|
|
140
|
+
]) {
|
|
141
|
+
for (const [name, range] of Object.entries(deps)) {
|
|
142
|
+
const have = installed[name];
|
|
143
|
+
if (!have) {
|
|
144
|
+
if (kind === 'npm')
|
|
145
|
+
missing.set(name, range);
|
|
146
|
+
else
|
|
147
|
+
peerNotes.add(`${name} (peer) — brak w projekcie, blok wymaga ${range}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// Specyfikatory bez konkretnej wersji (workspace:*, *, latest, git/file:) — nie oceniamy.
|
|
151
|
+
if (!/\d/.test(have))
|
|
152
|
+
continue;
|
|
153
|
+
if (satisfies(have, range))
|
|
154
|
+
continue;
|
|
155
|
+
if (kind === 'npm') {
|
|
156
|
+
throw new UserError(`Zależność ${name} jest w wersji ${have}, blok wymaga ${range}. ` +
|
|
157
|
+
`Zaktualizuj ją samodzielnie i uruchom ponownie.`);
|
|
158
|
+
}
|
|
159
|
+
peerNotes.add(`${name} (peer) jest w wersji ${have}, blok wymaga ${range}`);
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
162
|
}
|
|
140
163
|
if (missing.size > 0) {
|
|
141
|
-
|
|
164
|
+
// Do instalacji podajemy same nazwy — zakres bywa złożony (">=0.6.0 <1.0.0")
|
|
165
|
+
// i wklejony do shella by się rozpadł. Wymagania pokazujemy osobno.
|
|
166
|
+
log.warn(`Brakujące zależności npm — zainstaluj je: ${[...missing.keys()].join(' ')}`);
|
|
167
|
+
for (const [name, range] of missing)
|
|
168
|
+
log.step(`${name} wymaga ${range}`);
|
|
142
169
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const [maj = 0, min = 0, patch = 0] = version.replace(/^[^\d]*/, '').split('.').map((n) => parseInt(n, 10) || 0);
|
|
146
|
-
return [maj, min, patch];
|
|
147
|
-
}
|
|
148
|
-
/** Prosty test: czy `have` jest poniżej minimum z zakresu (`^`, `~`, `>=`). */
|
|
149
|
-
function belowMinimum(have, range) {
|
|
150
|
-
const [hM, hm, hp] = toTuple(have);
|
|
151
|
-
const [rM, rm, rp] = toTuple(range);
|
|
152
|
-
if (hM !== rM)
|
|
153
|
-
return hM < rM;
|
|
154
|
-
if (hm !== rm)
|
|
155
|
-
return hm < rm;
|
|
156
|
-
return hp < rp;
|
|
170
|
+
for (const note of peerNotes)
|
|
171
|
+
log.warn(note);
|
|
157
172
|
}
|
|
158
173
|
function printSummary(installed, metas, dryRun) {
|
|
159
174
|
log.info(`\n${dryRun ? 'Zainstalowałbym' : 'Zainstalowano'}: ${installed.join(', ')}`);
|
package/dist/semver.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Minimalna obsługa zakresów semver. CLI ma zero zależności runtime
|
|
2
|
+
// (package.json → "dependencies": {}), więc zamiast paczki `semver` niesiemy tyle
|
|
3
|
+
// kodu, ile pokrywa realne kształty zakresów z registry i z package.json aplikacji:
|
|
4
|
+
// ">=0.6.0 <1.0.0", "^22.0.0", "~1.2.3", "1.2.3", oraz alternatywy przez "||".
|
|
5
|
+
//
|
|
6
|
+
// Zasada bezpieczeństwa: czego nie rozumiemy, tego nie oceniamy. Zablokowanie
|
|
7
|
+
// instalacji z powodu składni nieznanej naszemu parserowi byłoby gorsze niż
|
|
8
|
+
// pominięcie kontroli — stąd `satisfies` zwraca `true` dla zakresów, których nie
|
|
9
|
+
// potrafi rozłożyć.
|
|
10
|
+
/** Wyciąga trójkę z wersji; toleruje prefiksy (`v1.2.3`) i sufiksy (`1.2.3-rc.1`). */
|
|
11
|
+
export function parseVersion(input) {
|
|
12
|
+
const m = /^[^\d]*(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(input.trim());
|
|
13
|
+
if (!m)
|
|
14
|
+
return null;
|
|
15
|
+
return [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
|
|
16
|
+
}
|
|
17
|
+
export function compare(a, b) {
|
|
18
|
+
for (let i = 0; i < 3; i++) {
|
|
19
|
+
if (a[i] !== b[i])
|
|
20
|
+
return a[i] < b[i] ? -1 : 1;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
/** Górna granica `^`: dla 0.x zawęża się do minora (0.6.x), a od 1.0 do majora. */
|
|
25
|
+
function caretUpper([maj, min, patch]) {
|
|
26
|
+
if (maj > 0)
|
|
27
|
+
return [maj + 1, 0, 0];
|
|
28
|
+
if (min > 0)
|
|
29
|
+
return [0, min + 1, 0];
|
|
30
|
+
return [0, 0, patch + 1];
|
|
31
|
+
}
|
|
32
|
+
/** Pojedynczy komparator. `null` = nie rozumiem tego kształtu. */
|
|
33
|
+
function satisfiesOne(version, comparator) {
|
|
34
|
+
const m = /^(>=|<=|>|<|=|\^|~|)\s*(.+)$/.exec(comparator.trim());
|
|
35
|
+
if (!m)
|
|
36
|
+
return null;
|
|
37
|
+
const [, op, rest] = m;
|
|
38
|
+
if (/[x*]/i.test(rest))
|
|
39
|
+
return null; // 1.x / 1.* — poza zakresem tego parsera
|
|
40
|
+
const target = parseVersion(rest);
|
|
41
|
+
if (!target)
|
|
42
|
+
return null;
|
|
43
|
+
switch (op) {
|
|
44
|
+
case '>=': return compare(version, target) >= 0;
|
|
45
|
+
case '<=': return compare(version, target) <= 0;
|
|
46
|
+
case '>': return compare(version, target) > 0;
|
|
47
|
+
case '<': return compare(version, target) < 0;
|
|
48
|
+
case '^': return compare(version, target) >= 0 && compare(version, caretUpper(target)) < 0;
|
|
49
|
+
case '~': return compare(version, target) >= 0 && compare(version, [target[0], target[1] + 1, 0]) < 0;
|
|
50
|
+
default: return compare(version, target) === 0; // '=' albo goła wersja
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Czy `version` mieści się w `range`. Obsługuje koniunkcję przez spację
|
|
55
|
+
* (">=0.6.0 <1.0.0") i alternatywę przez "||". Zakres, którego nie da się
|
|
56
|
+
* rozłożyć, traktujemy jako spełniony — patrz nagłówek pliku.
|
|
57
|
+
*/
|
|
58
|
+
export function satisfies(version, range) {
|
|
59
|
+
const parsed = parseVersion(version);
|
|
60
|
+
if (!parsed)
|
|
61
|
+
return true;
|
|
62
|
+
for (const alternative of range.split('||')) {
|
|
63
|
+
const comparators = alternative.trim().split(/\s+/).filter(Boolean);
|
|
64
|
+
if (comparators.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
const results = comparators.map((c) => satisfiesOne(parsed, c));
|
|
67
|
+
if (results.some((r) => r === null))
|
|
68
|
+
return true; // nieznany kształt — nie oceniamy
|
|
69
|
+
if (results.every(Boolean))
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|