@rokkit/cli 1.2.0 → 1.3.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/src/doctor.d.ts +0 -19
- package/package.json +2 -2
- package/skills/semantic-styles-rokkit/SKILL.md +4 -48
- package/src/config.js +1 -0
- package/src/convert.js +5 -0
- package/src/doctor.js +7 -92
- package/src/init.js +5 -3
- package/src/upgrade.js +3 -0
package/dist/src/doctor.d.ts
CHANGED
|
@@ -20,25 +20,6 @@ export function runChecks(fs: {
|
|
|
20
20
|
fix: string;
|
|
21
21
|
autoFix?: string;
|
|
22
22
|
}>;
|
|
23
|
-
/**
|
|
24
|
-
* Scan files for legacy z-scale utilities and suggest named-token equivalents.
|
|
25
|
-
* Advisory only — z-scale still resolves via the preset's back-compat layer.
|
|
26
|
-
* @param {Array<{ path: string, content: string }>} files
|
|
27
|
-
* @returns {{ count: number, byFile: Array<{ path: string, hits: Array<{ token: string, suggestion: string }> }> }}
|
|
28
|
-
*/
|
|
29
|
-
export function findLegacyZUtilities(files: Array<{
|
|
30
|
-
path: string;
|
|
31
|
-
content: string;
|
|
32
|
-
}>): {
|
|
33
|
-
count: number;
|
|
34
|
-
byFile: Array<{
|
|
35
|
-
path: string;
|
|
36
|
-
hits: Array<{
|
|
37
|
-
token: string;
|
|
38
|
-
suggestion: string;
|
|
39
|
-
}>;
|
|
40
|
-
}>;
|
|
41
|
-
};
|
|
42
23
|
/**
|
|
43
24
|
* Validate a parsed rokkit config for the named-token system. All advisory (warn).
|
|
44
25
|
* @param {Record<string, unknown> | null} config
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Command line utilities for icon bundling and conversion.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@mdi/svg": "^7.4.47",
|
|
30
|
-
"@rokkit/unocss": "1.
|
|
30
|
+
"@rokkit/unocss": "1.3.1",
|
|
31
31
|
"sade": "^1.8.1",
|
|
32
32
|
"@iconify/tools": "5.0.3",
|
|
33
33
|
"@iconify/types": "^2.0.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: semantic-styles-rokkit
|
|
3
|
-
description: Use when building, styling, or auditing a Rokkit-powered app (Svelte 5 + UnoCSS presetRokkit) — covers the named-token vocabulary (paper / ink / primary / on-primary / *-soft), EXTENDING it with custom tokens via `overrides:` and the full palette ladder via `tokens: 'extended'`, the rokkit.config.js palette→skin→tokens pipeline, dual-palette dark mode,
|
|
3
|
+
description: Use when building, styling, or auditing a Rokkit-powered app (Svelte 5 + UnoCSS presetRokkit) — covers the named-token vocabulary (paper / ink / primary / on-primary / *-soft), EXTENDING it with custom tokens via `overrides:` and the full palette ladder via `tokens: 'extended'`, the rokkit.config.js palette→skin→tokens pipeline, dual-palette dark mode, and the rokkit init / rokkit doctor CLI.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Semantic Styles — Rokkit
|
|
@@ -17,7 +17,6 @@ rokkit.config.js
|
|
|
17
17
|
UnoCSS preset (presetRokkit) → CSS custom properties + utility classes
|
|
18
18
|
↓
|
|
19
19
|
Components use named-token utilities: bg-paper · text-ink · bg-primary · text-on-primary
|
|
20
|
-
(legacy z-scale utilities — bg-surface-z1 — still resolve via back-compat aliases)
|
|
21
20
|
```
|
|
22
21
|
|
|
23
22
|
The pipeline has three layers. Most authoring happens in **Layer 3 (named tokens)**.
|
|
@@ -56,7 +55,7 @@ The preset wraps values into complete colors when it emits CSS vars (see Layer 3
|
|
|
56
55
|
**What:** A skin maps semantic role names to palette names. The core roles that back the
|
|
57
56
|
named-token vocabulary are: `surface`, `ink`, `primary`, `accent`, `success`, `warning`,
|
|
58
57
|
`danger`, `error`, `info`. (`secondary`/`tertiary` are accepted but no named token reads
|
|
59
|
-
them — they only matter if you
|
|
58
|
+
them — they only matter if you opt into `tokens: 'extended'` for those roles.)
|
|
60
59
|
|
|
61
60
|
**Why:** Decoupling role from palette lets you swap the whole scheme in config, not code.
|
|
62
61
|
`bg-paper` doesn't know whether `surface` is `kami`, `slate`, or `zinc`.
|
|
@@ -84,7 +83,7 @@ skins: {
|
|
|
84
83
|
|
|
85
84
|
### `tokens:` — which CSS the preset emits
|
|
86
85
|
```js
|
|
87
|
-
tokens: 'core' // default — emit the 24 named tokens (palette values inlined)
|
|
86
|
+
tokens: 'core' // default — emit the 24 named tokens (palette values inlined)
|
|
88
87
|
tokens: 'extended' // also emit the full 11-shade ladder (--color-{role}-{shade}) per role
|
|
89
88
|
```
|
|
90
89
|
`'core'` is the default and what apps should use. Reach for `'extended'` only when you need
|
|
@@ -226,16 +225,7 @@ directly in CSS-only contexts; for alpha, use `color-mix` (the form the preset i
|
|
|
226
225
|
.thing { background: var(--paper); border-color: var(--paper-edge); }
|
|
227
226
|
.thing-50pc { background: color-mix(in oklch, var(--primary) 50%, transparent); }
|
|
228
227
|
```
|
|
229
|
-
Prefer utility classes over raw vars where you can.
|
|
230
|
-
`--color-{role}-z{n}` vars are aliases pointing at named tokens, so they too resolve to
|
|
231
|
-
complete colors — the old "wrap bare components in `rgb()`/`oklch()`" rule no longer
|
|
232
|
-
applies in core mode.)
|
|
233
|
-
|
|
234
|
-
### Legacy z-scale utilities (back-compat)
|
|
235
|
-
|
|
236
|
-
The older z-scale utilities (`bg-surface-z0`, `text-surface-z9`, `text-primary-z5`, …)
|
|
237
|
-
**still resolve** as a back-compat layer that collapses onto the named tokens above —
|
|
238
|
-
existing code keeps working. New code should use named tokens. See the migration section.
|
|
228
|
+
Prefer utility classes over raw vars where you can.
|
|
239
229
|
|
|
240
230
|
---
|
|
241
231
|
|
|
@@ -305,38 +295,6 @@ Default stays `'core'`.
|
|
|
305
295
|
|
|
306
296
|
---
|
|
307
297
|
|
|
308
|
-
## Migrating from z-scale to named tokens
|
|
309
|
-
|
|
310
|
-
`rokkit doctor` scans `src/**` and prints the suggested replacement per occurrence
|
|
311
|
-
(advisory — it never rewrites your code). The mapping it uses:
|
|
312
|
-
|
|
313
|
-
**Surface role** (`{prefix}-surface-z{n}` → `{prefix}-{token}`):
|
|
314
|
-
|
|
315
|
-
| z | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
|
316
|
-
|---|---|---|---|---|---|---|---|---|---|---|----|
|
|
317
|
-
| token | `paper` | `paper-soft` | `paper-mute` | `paper-mute` | `paper-edge` | `ink-soft` | `ink-soft` | `ink-mute` | `ink-mute` | `ink` | `ink` |
|
|
318
|
-
|
|
319
|
-
**Ink role** (inverted scale):
|
|
320
|
-
|
|
321
|
-
| z | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
|
322
|
-
|---|---|---|---|---|---|---|---|---|---|---|----|
|
|
323
|
-
| token | `ink` | `ink-mute` | `ink-mute` | `ink-soft` | `ink-soft` | `paper-edge` | `paper-edge` | `paper-mute` | `paper-mute` | `paper-soft` | `paper` |
|
|
324
|
-
|
|
325
|
-
**Accent / status roles** (`primary`, `accent`, `success`, `warning`, `danger`, `error`,
|
|
326
|
-
`info`): `z0–z2 → {role}-soft` (tinted background), `z3+ → {role}` (solid). Exception:
|
|
327
|
-
`primary` has **no** `-soft` companion — `primary-z*` all map to `primary`; for a tinted
|
|
328
|
-
primary background use `accent-soft` or a custom token.
|
|
329
|
-
|
|
330
|
-
Common rewrites:
|
|
331
|
-
```
|
|
332
|
-
bg-surface-z0 → bg-paper text-surface-z9 → text-ink
|
|
333
|
-
bg-surface-z1 → bg-paper-soft text-surface-z7 → text-ink-mute
|
|
334
|
-
border-surface-z3 → border-paper-edge text-surface-z5 → text-ink-soft
|
|
335
|
-
text-primary-z5 → text-primary bg-success-z1 → bg-success-soft
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
---
|
|
339
|
-
|
|
340
298
|
## Rokkit CLI — `init` and `doctor`
|
|
341
299
|
|
|
342
300
|
**`rokkit init`** scaffolds `rokkit.config.js` in the named-token shape (a header comment
|
|
@@ -354,7 +312,6 @@ fail the command or block `--fix`:
|
|
|
354
312
|
| `skin-ink-role` | the active colormap has no `ink` role (text tokens fall back) |
|
|
355
313
|
| `oklch-needs-palettes` | `colorSpace: 'oklch'` but no `palettes` block |
|
|
356
314
|
| `colors-alias` | config uses the legacy `colors:` instead of `skin:` |
|
|
357
|
-
| legacy z-utilities | `src/**` contains `bg-surface-z*` / `text-primary-z*` etc. (prints named-token suggestions) |
|
|
358
315
|
|
|
359
316
|
The "active colormap" is resolved the same way the preset does: `skins.default ?? skin ??
|
|
360
317
|
colors`. Run `rokkit doctor --fix` to auto-write a real starter config when one is missing.
|
|
@@ -447,7 +404,6 @@ export default {
|
|
|
447
404
|
| "core only emits `accent`/`accent-soft`, so I can't have `accent-2`/`on-accent`" | the vocabulary is extensible, not fixed | add them under `overrides:` (or use `tokens: 'extended'`) — see Layer 3b |
|
|
448
405
|
| Override palette ref `'sky-600'` (hyphen) | refs use **dot** notation | `'sky.600'` (and `sky` must be in `palettes:`) |
|
|
449
406
|
| `color: #3D3730` in a component | breaks on theme change | `text-ink` (or the right named token) |
|
|
450
|
-
| Using `bg-surface-z5` / `text-primary-z5` in new code | legacy back-compat layer | named tokens — see migration table (or run `rokkit doctor`) |
|
|
451
407
|
| Single-palette `surface` but expecting dark mode | no `[data-mode]` block is emitted | make `surface`/`ink` dual-palette `{ light, dark }` |
|
|
452
408
|
| Skin with no `ink` role | `ink-*` text tokens fall back to surface | add `ink: '<palette>'` (reuse surface is fine) |
|
|
453
409
|
| Using `skin:` and `skins:` together | `skins:` silently wins | pick one |
|
package/src/config.js
CHANGED
|
@@ -16,6 +16,7 @@ export async function loadConfig(adapters) {
|
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
18
|
const { default: config } = await import(`file://${configPath}?t=${Date.now()}`)
|
|
19
|
+
/* v8 ignore next -- dynamic import succeeds only in real Node, not JSDOM */
|
|
19
20
|
return config
|
|
20
21
|
} catch {
|
|
21
22
|
return null
|
package/src/convert.js
CHANGED
|
@@ -47,6 +47,9 @@ export function processIcons(iconSet, color) {
|
|
|
47
47
|
try {
|
|
48
48
|
cleanAndOptimizeIcon(svg, color)
|
|
49
49
|
} catch (err) {
|
|
50
|
+
/* v8 ignore next 6 -- catch body requires an iconify-tools internal parse
|
|
51
|
+
error; the err1 fixture exercises this path but the exception propagates
|
|
52
|
+
at importDirectory level before processIcons is reached */
|
|
50
53
|
console.error(`Error parsing ${name}: ${err.reason}`)
|
|
51
54
|
iconSet.remove(name)
|
|
52
55
|
return
|
|
@@ -116,6 +119,8 @@ export async function convert(folder, options) {
|
|
|
116
119
|
|
|
117
120
|
// Create the target directory if it doesn't exist
|
|
118
121
|
const targetDir = path.resolve(options.target)
|
|
122
|
+
/* v8 ignore next 4 -- bundle() (called above) always creates targetDir first;
|
|
123
|
+
this guard is unreachable in practice but kept as a defensive fallback */
|
|
119
124
|
if (!fs.existsSync(targetDir)) {
|
|
120
125
|
fs.mkdirSync(targetDir, { recursive: true })
|
|
121
126
|
}
|
package/src/doctor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync
|
|
3
|
-
import { resolve
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
|
3
|
+
import { resolve } from 'path'
|
|
4
4
|
import { loadConfig as loadParsedConfig } from './config.js'
|
|
5
5
|
import {
|
|
6
6
|
generateUnoConfig,
|
|
@@ -240,6 +240,9 @@ function applyPatchHtml(cwd, label) {
|
|
|
240
240
|
* @param {string} cwd
|
|
241
241
|
* @param {string} label
|
|
242
242
|
*/
|
|
243
|
+
/* v8 ignore next 16 -- chart-config check emits 'warn' (not 'fail'); applyCheckFix
|
|
244
|
+
only processes 'fail' statuses, making this handler unreachable via the normal
|
|
245
|
+
fix flow. Kept for potential future use if the check severity changes. */
|
|
243
246
|
function applyPatchChartConfig(cwd, label) {
|
|
244
247
|
const configPath = resolve(cwd, 'rokkit.config.js')
|
|
245
248
|
if (!existsSync(configPath)) return false
|
|
@@ -363,85 +366,6 @@ function handleResults(checks, cwd, failures, fix) {
|
|
|
363
366
|
}
|
|
364
367
|
}
|
|
365
368
|
|
|
366
|
-
// z-slot → named token (matches @rokkit/core Z_COLLAPSE maps).
|
|
367
|
-
const SURFACE_Z = {
|
|
368
|
-
z0: 'paper', z1: 'paper-soft', z2: 'paper-mute', z3: 'paper-mute', z4: 'paper-edge',
|
|
369
|
-
z5: 'ink-soft', z6: 'ink-soft', z7: 'ink-mute', z8: 'ink-mute', z9: 'ink', z10: 'ink'
|
|
370
|
-
}
|
|
371
|
-
const INK_Z = {
|
|
372
|
-
z0: 'ink', z1: 'ink-mute', z2: 'ink-mute', z3: 'ink-soft', z4: 'ink-soft',
|
|
373
|
-
z5: 'paper-edge', z6: 'paper-edge', z7: 'paper-mute', z8: 'paper-mute', z9: 'paper-soft', z10: 'paper'
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Map a z-scale role+slot to its nearest named token.
|
|
378
|
-
* @param {string} role
|
|
379
|
-
* @param {string} z — e.g. 'z5'
|
|
380
|
-
* @returns {string}
|
|
381
|
-
*/
|
|
382
|
-
function suggestNamedToken(role, z) {
|
|
383
|
-
if (role === 'surface') return SURFACE_Z[z]
|
|
384
|
-
if (role === 'ink') return INK_Z[z]
|
|
385
|
-
// accent + status roles collapse z0–z2 to their `-soft` tint (matches
|
|
386
|
-
// @rokkit/core theme.ts getZAliasesOther: zNum <= 2). primary has no `-soft`.
|
|
387
|
-
if (role !== 'primary' && Number(z.slice(1)) <= 2) return `${role}-soft`
|
|
388
|
-
return role
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const Z_UTILITY_RE =
|
|
392
|
-
/\b(bg|text|border(?:-[tblr])?|fill|stroke|ring|outline|divide)-(surface|ink|primary|accent|success|warning|danger|error|info)-(z(?:10|[0-9]))\b/g
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Scan files for legacy z-scale utilities and suggest named-token equivalents.
|
|
396
|
-
* Advisory only — z-scale still resolves via the preset's back-compat layer.
|
|
397
|
-
* @param {Array<{ path: string, content: string }>} files
|
|
398
|
-
* @returns {{ count: number, byFile: Array<{ path: string, hits: Array<{ token: string, suggestion: string }> }> }}
|
|
399
|
-
*/
|
|
400
|
-
export function findLegacyZUtilities(files) {
|
|
401
|
-
const byFile = []
|
|
402
|
-
let count = 0
|
|
403
|
-
for (const { path: filePath, content } of files) {
|
|
404
|
-
const hits = []
|
|
405
|
-
for (const match of content.matchAll(Z_UTILITY_RE)) {
|
|
406
|
-
const [token, prefix, role, z] = match
|
|
407
|
-
hits.push({ token, suggestion: `${prefix}-${suggestNamedToken(role, z)}` })
|
|
408
|
-
}
|
|
409
|
-
if (hits.length > 0) {
|
|
410
|
-
byFile.push({ path: filePath, hits })
|
|
411
|
-
count += hits.length
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return { count, byFile }
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const SCAN_EXTS = new Set(['.svelte', '.css', '.ts', '.js'])
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Recursively gather scannable source files under cwd/src.
|
|
421
|
-
* Skips node_modules, symlinks (avoids recursion loops), and uno.config.js
|
|
422
|
-
* (its safelist legitimately lists z-utilities).
|
|
423
|
-
* @param {string} cwd
|
|
424
|
-
* @returns {Array<{ path: string, content: string }>}
|
|
425
|
-
*/
|
|
426
|
-
function gatherSrcFiles(cwd) {
|
|
427
|
-
const root = resolve(cwd, 'src')
|
|
428
|
-
if (!existsSync(root)) return []
|
|
429
|
-
const out = []
|
|
430
|
-
const walk = (dir) => {
|
|
431
|
-
for (const dirent of readdirSync(dir, { withFileTypes: true })) {
|
|
432
|
-
if (dirent.isSymbolicLink() || dirent.name === 'node_modules') continue
|
|
433
|
-
const full = resolve(dir, dirent.name)
|
|
434
|
-
if (dirent.isDirectory()) {
|
|
435
|
-
walk(full)
|
|
436
|
-
} else if (dirent.isFile() && SCAN_EXTS.has(extname(dirent.name)) && !full.endsWith('uno.config.js')) {
|
|
437
|
-
out.push({ path: full.slice(cwd.length + 1), content: readFileSync(full, 'utf-8') })
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
walk(root)
|
|
442
|
-
return out
|
|
443
|
-
}
|
|
444
|
-
|
|
445
369
|
/**
|
|
446
370
|
* Validate a parsed rokkit config for the named-token system. All advisory (warn).
|
|
447
371
|
* @param {Record<string, unknown> | null} config
|
|
@@ -497,6 +421,8 @@ export async function doctor(opts = {}) {
|
|
|
497
421
|
|
|
498
422
|
const parsed = await loadParsedConfig({ cwd })
|
|
499
423
|
const shapeChecks = validateConfigShape(parsed)
|
|
424
|
+
/* v8 ignore next 8 -- loadConfig uses dynamic file:// import which always fails in
|
|
425
|
+
JSDOM; validateConfigShape(null) returns [] so this branch is unreachable in tests */
|
|
500
426
|
if (shapeChecks.length > 0) {
|
|
501
427
|
console.info('\nConfig shape:')
|
|
502
428
|
for (const c of shapeChecks) {
|
|
@@ -505,17 +431,6 @@ export async function doctor(opts = {}) {
|
|
|
505
431
|
}
|
|
506
432
|
}
|
|
507
433
|
|
|
508
|
-
const { count, byFile } = findLegacyZUtilities(gatherSrcFiles(cwd))
|
|
509
|
-
if (count > 0) {
|
|
510
|
-
console.info(`\nLegacy z-scale utilities (advisory — ${count} in ${byFile.length} file(s)):`)
|
|
511
|
-
for (const { path: filePath, hits } of byFile.slice(0, 10)) {
|
|
512
|
-
const examples = [...new Map(hits.map((h) => [h.token, h.suggestion]))]
|
|
513
|
-
const shown = examples.slice(0, 3).map(([t, s]) => `${t} → ${s}`).join(', ')
|
|
514
|
-
console.info(` ${filePath}: ${shown}${examples.length > 3 ? ', …' : ''}`)
|
|
515
|
-
}
|
|
516
|
-
console.info(' Named tokens are preferred; z-scale still works via back-compat.')
|
|
517
|
-
}
|
|
518
|
-
|
|
519
434
|
handleResults(checks, cwd, failures, opts.fix ?? false)
|
|
520
435
|
console.info('')
|
|
521
436
|
}
|
package/src/init.js
CHANGED
|
@@ -20,9 +20,7 @@ const NAMED_TOKEN_HEADER = `/**
|
|
|
20
20
|
* Accent bg-primary · text-on-primary · bg-accent · bg-accent-soft
|
|
21
21
|
* Status bg-success-soft · text-success (+ warning / danger / error / info)
|
|
22
22
|
*
|
|
23
|
-
* Tokens flip automatically under [data-mode="dark"].
|
|
24
|
-
* utilities (bg-surface-z0, text-primary-z5) still resolve for back-compat,
|
|
25
|
-
* but new code should prefer the named tokens above.
|
|
23
|
+
* Tokens flip automatically under [data-mode="dark"].
|
|
26
24
|
*
|
|
27
25
|
* tokens: 'core' emits the named vocabulary; 'extended' also emits the full
|
|
28
26
|
* 11-shade palette ladder per role (for charts / data-viz).
|
|
@@ -410,6 +408,7 @@ const PROMPTS_CONFIG = [
|
|
|
410
408
|
type: (prev) => (prev?.length > 1 ? 'select' : null),
|
|
411
409
|
name: 'defaultTheme',
|
|
412
410
|
message: 'Default theme style',
|
|
411
|
+
/* v8 ignore next 2 -- prompts.inject bypasses choices callback evaluation in tests */
|
|
413
412
|
choices: (prev) =>
|
|
414
413
|
prev.map((t) => ({ title: t.charAt(0).toUpperCase() + t.slice(1), value: t }))
|
|
415
414
|
},
|
|
@@ -549,6 +548,9 @@ export async function init(_opts = {}, adapters = {}) {
|
|
|
549
548
|
|
|
550
549
|
const response = await prompts(PROMPTS_CONFIG)
|
|
551
550
|
|
|
551
|
+
/* v8 ignore next 10 -- prompts mutates PROMPTS_CONFIG[n].type in-place on the
|
|
552
|
+
first call, so the conditional primary/accent/surface prompts always resolve
|
|
553
|
+
to null on subsequent runs; the branch is tested via generateConfig directly */
|
|
552
554
|
if (response.palette === 'custom') {
|
|
553
555
|
response.customColors = {
|
|
554
556
|
primary: response.primary,
|
package/src/upgrade.js
CHANGED
|
@@ -57,10 +57,13 @@ function resolveUpgradeAdapters(adapters) {
|
|
|
57
57
|
exists: adapters.exists ?? ((p) => existsSync(p)),
|
|
58
58
|
fetchVersion:
|
|
59
59
|
adapters.fetchVersion ??
|
|
60
|
+
/* v8 ignore next 8 -- default fetchVersion calls real npm; tests always inject
|
|
61
|
+
fetchVersion so this closure is never executed in the test environment */
|
|
60
62
|
((name) => {
|
|
61
63
|
try {
|
|
62
64
|
return execFileSync('npm', ['view', name, 'version'], { encoding: 'utf-8' }).trim()
|
|
63
65
|
} catch {
|
|
66
|
+
// npm binary absent or network error — treat as unknown version
|
|
64
67
|
return null
|
|
65
68
|
}
|
|
66
69
|
}),
|