@krapwoo/ds-viewer 0.6.7 → 0.6.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 +10 -0
- package/dist/cli/dev.js +18 -2
- package/dist/cli/packageManager.js +41 -6
- package/dist/cli/update.js +28 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -622,6 +622,16 @@ Downloads the target version, runs its own `migrate --from <your version>`, upgr
|
|
|
622
622
|
whichever package manager your lockfile names, re-runs `migrate` for real, then `doctor`. Changes
|
|
623
623
|
are left uncommitted — review them like any other change.
|
|
624
624
|
|
|
625
|
+
The upgrade keeps the package in whichever section your `package.json` already declares it in,
|
|
626
|
+
rather than always writing a devDependency.
|
|
627
|
+
|
|
628
|
+
If that spec is a local one — `file:`, `link:`, `workspace:`, `portal:`, or a bare path — `update`
|
|
629
|
+
refuses and tells you how to proceed, because installing a published version would replace the
|
|
630
|
+
checkout your project deliberately points at. The catalog would keep running while no longer
|
|
631
|
+
reflecting the source you are editing, which is hard to notice. `--force` does not override this:
|
|
632
|
+
it exists for uncommitted files, not for changing where the package comes from. The update page's
|
|
633
|
+
**Update now** refuses the same way, reporting it as a failed install step.
|
|
634
|
+
|
|
625
635
|
### `npx ds-viewer migrate --from <version>`
|
|
626
636
|
|
|
627
637
|
Runs every migration introduced after `<version>` — `update` calls this for you; run it directly
|
package/dist/cli/dev.js
CHANGED
|
@@ -14,7 +14,8 @@ import { readOwnVersion } from "./packageVersion.js";
|
|
|
14
14
|
import { createUpdateEndpoint } from "./endpoint.js";
|
|
15
15
|
import { buildUpdatePlan } from "./updatePlan.js";
|
|
16
16
|
import { lastSummaryLine } from "./doctor.js";
|
|
17
|
-
import { detectPackageManager, installUpgradeCommand, platformCommand } from "./packageManager.js";
|
|
17
|
+
import { declaredDependency, detectPackageManager, installUpgradeCommand, isLocalSpec, platformCommand } from "./packageManager.js";
|
|
18
|
+
import { localInstallRefusal } from "./update.js";
|
|
18
19
|
import { DEFAULT_WORKSPACE_DIR, RENDER_WORKSPACE_DIR } from "./types.js";
|
|
19
20
|
export class LocalInstallMissingError extends Error {
|
|
20
21
|
}
|
|
@@ -142,7 +143,22 @@ export async function performUpdate(config, currentVersion, plan, deps) {
|
|
|
142
143
|
});
|
|
143
144
|
report(startAt);
|
|
144
145
|
if (startAt <= 1) {
|
|
145
|
-
|
|
146
|
+
// The same refusal the CLI gives: installing a registry version over a `file:`/`link:`/
|
|
147
|
+
// `workspace:` spec would replace the local checkout the project points at, and the catalog
|
|
148
|
+
// would keep serving while no longer reflecting the source being edited. Reported as a
|
|
149
|
+
// failure of the install step, so the page offers its normal recovery rather than appearing
|
|
150
|
+
// to succeed.
|
|
151
|
+
const declared = declaredDependency(config.projectRoot, '@krapwoo/ds-viewer');
|
|
152
|
+
if (declared && isLocalSpec(declared.spec)) {
|
|
153
|
+
deps.onStatus({
|
|
154
|
+
phase: 'failure',
|
|
155
|
+
log: localInstallRefusal(declared.spec, plan.latest),
|
|
156
|
+
failedStep: stepDefs[1].failLabel ?? stepDefs[1].label,
|
|
157
|
+
step: 'install',
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const { command, args } = installUpgradeCommand(pm, '@krapwoo/ds-viewer', plan.latest, declared?.section);
|
|
146
162
|
try {
|
|
147
163
|
await deps.execImpl(command, args, { cwd: config.projectRoot, encoding: 'utf8' });
|
|
148
164
|
}
|
|
@@ -15,17 +15,52 @@ export function detectPackageManager(projectRoot) {
|
|
|
15
15
|
}
|
|
16
16
|
return 'npm'; // package-lock.json, or no lockfile at all (npm's own documented default either way).
|
|
17
17
|
}
|
|
18
|
+
/** How `pkg` is declared in this project, or undefined when it is not declared at all.
|
|
19
|
+
* `dependencies` wins when both sections name it, matching npm's own resolution. */
|
|
20
|
+
export function declaredDependency(projectRoot, pkg) {
|
|
21
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
22
|
+
if (!existsSync(packageJsonPath))
|
|
23
|
+
return undefined;
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined; // An unreadable package.json is not this function's error to report.
|
|
30
|
+
}
|
|
31
|
+
const fromDeps = parsed.dependencies?.[pkg];
|
|
32
|
+
if (typeof fromDeps === 'string')
|
|
33
|
+
return { section: 'dependencies', spec: fromDeps };
|
|
34
|
+
const fromDevDeps = parsed.devDependencies?.[pkg];
|
|
35
|
+
if (typeof fromDevDeps === 'string')
|
|
36
|
+
return { section: 'devDependencies', spec: fromDevDeps };
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
/** A spec that resolves to something other than a published version: the local source tree, a
|
|
40
|
+
* linked folder, or another package in the same workspace. Installing a registry version over one
|
|
41
|
+
* of these replaces the thing the project is deliberately pointing at, so `update` refuses rather
|
|
42
|
+
* than doing it silently. `npm:` aliases and plain ranges are registry-backed and not local. */
|
|
43
|
+
export function isLocalSpec(spec) {
|
|
44
|
+
const value = spec.trim();
|
|
45
|
+
if (/^(file|link|workspace|portal):/i.test(value))
|
|
46
|
+
return true;
|
|
47
|
+
// A bare relative or absolute path is a file dependency too — npm accepts `../pkg` unprefixed.
|
|
48
|
+
return value.startsWith('./') || value.startsWith('../') || value.startsWith('/') || value.startsWith('~/');
|
|
49
|
+
}
|
|
18
50
|
/** The one command `cli/update.ts`'s "Upgrades with the project's package manager" step (design
|
|
19
51
|
* §5 "`npx ds-viewer update`" step 3) runs, for whichever manager `detectPackageManager` found.
|
|
20
|
-
* Yarn (classic and Berry both) use the same `yarn add
|
|
21
|
-
*
|
|
22
|
-
|
|
52
|
+
* Yarn (classic and Berry both) use the same `yarn add` syntax for upgrading a single package to
|
|
53
|
+
* an exact version. `section` keeps the package in the section it is already declared in; it
|
|
54
|
+
* defaults to `devDependencies`, which is where `init` puts it. */
|
|
55
|
+
export function installUpgradeCommand(pm, pkg, version, section = 'devDependencies') {
|
|
23
56
|
const spec = `${pkg}@${version}`;
|
|
57
|
+
const dev = section === 'devDependencies';
|
|
24
58
|
if (pm === 'npm')
|
|
25
|
-
return { command: 'npm', args: ['install', '--save-dev', spec] };
|
|
59
|
+
return { command: 'npm', args: ['install', dev ? '--save-dev' : '--save-prod', spec] };
|
|
26
60
|
if (pm === 'pnpm')
|
|
27
|
-
return { command: 'pnpm', args: ['add', '--save-dev', spec] };
|
|
28
|
-
|
|
61
|
+
return { command: 'pnpm', args: ['add', dev ? '--save-dev' : '--save-prod', spec] };
|
|
62
|
+
// Yarn has no --save-prod: a plain `yarn add` is already a production dependency.
|
|
63
|
+
return { command: 'yarn', args: dev ? ['add', '--dev', spec] : ['add', spec] };
|
|
29
64
|
}
|
|
30
65
|
const WINDOWS_SHIMS = new Set(['npm', 'npx', 'pnpm', 'yarn']);
|
|
31
66
|
/** On Windows, `npm`, `npx`, `pnpm` and `yarn` are `.cmd` shims, which Node refuses to run
|
package/dist/cli/update.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createInterface } from 'node:readline/promises';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { buildUpdatePlan, isAlreadyUpToDate } from "./updatePlan.js";
|
|
7
|
-
import { detectPackageManager, installUpgradeCommand, platformCommand } from "./packageManager.js";
|
|
7
|
+
import { declaredDependency, detectPackageManager, installUpgradeCommand, isLocalSpec, platformCommand } from "./packageManager.js";
|
|
8
8
|
import { readOwnVersion } from "./packageVersion.js";
|
|
9
9
|
import { lastSummaryLine } from "./doctor.js";
|
|
10
10
|
function formatPlanHuman(plan) {
|
|
@@ -32,6 +32,22 @@ async function defaultConfirm(message) {
|
|
|
32
32
|
function installedMainJs(projectRoot) {
|
|
33
33
|
return path.join(projectRoot, 'node_modules', '@krapwoo', 'ds-viewer', 'dist', 'cli', 'main.js');
|
|
34
34
|
}
|
|
35
|
+
/** Why an update cannot run over a local checkout, and what to do instead. Shared by the CLI and
|
|
36
|
+
* the update page so both refuse in the same words. */
|
|
37
|
+
export function localInstallRefusal(spec, latest) {
|
|
38
|
+
return [
|
|
39
|
+
`Refusing: @krapwoo/ds-viewer is installed from "${spec}", not from the registry.`,
|
|
40
|
+
'',
|
|
41
|
+
'Updating would install the published package over it, replacing the local checkout this',
|
|
42
|
+
'project deliberately points at — the catalog would keep running while no longer reflecting',
|
|
43
|
+
'the source being edited.',
|
|
44
|
+
'',
|
|
45
|
+
`To move this project onto the published ${latest}, change the dependency yourself:`,
|
|
46
|
+
` npm install --save-dev @krapwoo/ds-viewer@${latest}`,
|
|
47
|
+
'',
|
|
48
|
+
'To stay on local source, pull and rebuild it instead.',
|
|
49
|
+
].join('\n');
|
|
50
|
+
}
|
|
35
51
|
/** Design §5 "`npx ds-viewer update`", steps 1-6. */
|
|
36
52
|
export async function runUpdate(config, options = {}) {
|
|
37
53
|
const currentVersion = options.currentVersion ?? readOwnVersion(path.dirname(fileURLToPath(import.meta.url)));
|
|
@@ -41,6 +57,14 @@ export async function runUpdate(config, options = {}) {
|
|
|
41
57
|
const planText = formatPlanHuman(plan);
|
|
42
58
|
if (options.dryRun)
|
|
43
59
|
return { output: planText, exitCode: 0 };
|
|
60
|
+
// Refused before the dirty-file check and before any prompt: a `--yes --force` script must not
|
|
61
|
+
// be able to swap a local checkout for a registry copy either. `--force` deliberately does not
|
|
62
|
+
// override this — it exists for uncommitted files, not for changing where the package comes
|
|
63
|
+
// from, which is a decision the project's own package.json already recorded.
|
|
64
|
+
const declared = declaredDependency(config.projectRoot, '@krapwoo/ds-viewer');
|
|
65
|
+
if (declared && isLocalSpec(declared.spec)) {
|
|
66
|
+
return { output: `${planText}\n\n${localInstallRefusal(declared.spec, plan.latest)}`, exitCode: 1 };
|
|
67
|
+
}
|
|
44
68
|
// Step 2: refuses on any dirty planned file, unless --force — checked before the confirmation
|
|
45
69
|
// prompt, so a script passing --yes without --force still gets refused instead of silently
|
|
46
70
|
// overwriting uncommitted work.
|
|
@@ -59,7 +83,9 @@ export async function runUpdate(config, options = {}) {
|
|
|
59
83
|
const run = platformCommand(cmd, args);
|
|
60
84
|
return execFileSync(run.command, run.args, { ...opts, encoding: 'utf8', shell: run.shell });
|
|
61
85
|
});
|
|
62
|
-
const { command, args } = installUpgradeCommand(detectPackageManager(config.projectRoot), '@krapwoo/ds-viewer', plan.latest
|
|
86
|
+
const { command, args } = installUpgradeCommand(detectPackageManager(config.projectRoot), '@krapwoo/ds-viewer', plan.latest,
|
|
87
|
+
// Keep it in the section the project already declared, instead of forcing devDependencies.
|
|
88
|
+
declared?.section);
|
|
63
89
|
try {
|
|
64
90
|
execImpl(command, args, { cwd: config.projectRoot, encoding: 'utf8' });
|
|
65
91
|
}
|