@sparkelf/dsh-plus 0.1.0-rc.37 → 0.1.0-rc.39
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/lib/bin.js
CHANGED
|
@@ -401,16 +401,63 @@ function alignReplacedPackageNames(profileModules) {
|
|
|
401
401
|
renameSync(temporary, manifestPath);
|
|
402
402
|
}
|
|
403
403
|
}
|
|
404
|
-
/**
|
|
404
|
+
/**
|
|
405
|
+
* Run pnpm in one directory, inheriting its output.
|
|
406
|
+
*
|
|
407
|
+
* Windows resolves a command through its shell: \`spawnSync\` on a \`.cmd\` shim fails
|
|
408
|
+
* with EINVAL, so the call goes through \`cmd.exe\` there. This matches the runner in
|
|
409
|
+
* \`apply.ts\`, which the apply path has used on Windows since it shipped.
|
|
410
|
+
*
|
|
411
|
+
* @param cwd - the directory pnpm runs in.
|
|
412
|
+
* @param args - pnpm arguments, without the executable.
|
|
413
|
+
* @param label - the failing step's name, for the error.
|
|
414
|
+
*/
|
|
405
415
|
function runPnpm(cwd, args, label) {
|
|
406
|
-
const result = spawnSync(
|
|
416
|
+
const result = spawnSync("pnpm", [...args], {
|
|
407
417
|
cwd,
|
|
408
|
-
stdio: "inherit"
|
|
418
|
+
stdio: "inherit",
|
|
419
|
+
shell: process.platform === "win32"
|
|
409
420
|
});
|
|
410
421
|
if (result.error !== void 0) throw result.error;
|
|
411
422
|
if (result.status !== 0) throw new Error(label + " failed with exit code " + String(result.status));
|
|
412
423
|
}
|
|
413
424
|
/**
|
|
425
|
+
* Report whether pnpm can run.
|
|
426
|
+
*
|
|
427
|
+
* The profile installs its own tree so its \`overrides\` apply, and only pnpm reads those
|
|
428
|
+
* from a workspace, so pnpm is a prerequisite the distribution cannot supply. Probing
|
|
429
|
+
* first turns a cryptic failure from the install into a message naming what is missing.
|
|
430
|
+
*
|
|
431
|
+
* @returns \`true\` when pnpm answers with a version.
|
|
432
|
+
*/
|
|
433
|
+
function pnpmAvailable() {
|
|
434
|
+
return spawnSync("pnpm", ["--version"], {
|
|
435
|
+
stdio: "pipe",
|
|
436
|
+
shell: process.platform === "win32",
|
|
437
|
+
encoding: "utf8"
|
|
438
|
+
}).status === 0;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* The commands that install pnpm, in the order worth trying.
|
|
442
|
+
*
|
|
443
|
+
* Corepack ships with Node and needs no download, so it comes first; npm is the fallback
|
|
444
|
+
* for an installation whose Corepack is absent or disabled. Both are the consumer's own
|
|
445
|
+
* toolchain, which is what lets the first start offer to install rather than only report.
|
|
446
|
+
*
|
|
447
|
+
* @returns commands to try in order, stopping at the first that works.
|
|
448
|
+
*/
|
|
449
|
+
function pnpmInstallCommands() {
|
|
450
|
+
return ["corepack enable pnpm", "npm install -g pnpm"];
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* The command to show a consumer who declines the automatic install.
|
|
454
|
+
*
|
|
455
|
+
* @returns the preferred command, which is the one the offer runs first.
|
|
456
|
+
*/
|
|
457
|
+
function pnpmInstallCommand() {
|
|
458
|
+
return pnpmInstallCommands()[0] ?? "npm install -g pnpm";
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
414
461
|
* Expose the distribution's nested packages at the top level the profile searches.
|
|
415
462
|
*
|
|
416
463
|
* The profile resolves a bundle from one directory, so a package npm nested under the
|
|
@@ -687,6 +734,30 @@ async function start(argv) {
|
|
|
687
734
|
const options = parseStartOptions(argv);
|
|
688
735
|
const anchor = installationAnchor();
|
|
689
736
|
const paths = resolvePaths(anchor);
|
|
737
|
+
if (!pnpmAvailable()) {
|
|
738
|
+
const command = pnpmInstallCommand();
|
|
739
|
+
console.log("pnpm is required to install the plus profile, and was not found.");
|
|
740
|
+
console.log("Install it with: " + command);
|
|
741
|
+
if (!await confirm("Install pnpm now?")) {
|
|
742
|
+
console.log("Install pnpm and run dsh-plus start again.");
|
|
743
|
+
return 1;
|
|
744
|
+
}
|
|
745
|
+
let installed = false;
|
|
746
|
+
for (const candidate of pnpmInstallCommands()) if (spawnSync(candidate, {
|
|
747
|
+
stdio: "inherit",
|
|
748
|
+
shell: true
|
|
749
|
+
}).status === 0 && pnpmAvailable()) {
|
|
750
|
+
installed = true;
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
if (!installed) {
|
|
754
|
+
console.log("Could not install pnpm automatically.");
|
|
755
|
+
console.log("Run one of these, then run dsh-plus start again:");
|
|
756
|
+
for (const candidate of pnpmInstallCommands()) console.log(" " + candidate);
|
|
757
|
+
return 1;
|
|
758
|
+
}
|
|
759
|
+
console.log("pnpm installed.");
|
|
760
|
+
}
|
|
690
761
|
const created = ensureProfile(paths, installationRoot());
|
|
691
762
|
console.log(created ? "Created the plus profile at " + paths.profileDirectory : "Using the existing plus profile");
|
|
692
763
|
for (const label of applyProfileNpmPatches(paths.distributionDirectory, paths.profileDirectory)) console.log("Applied the reviewed patch " + label);
|
|
@@ -14,7 +14,7 @@ import { dirname, join } from 'node:path';
|
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import { newerVersion } from "./registry-versions.js";
|
|
16
16
|
import { DEFAULT_PORT, STOP_GRACE_MILLISECONDS, choosePort, clearState, isRunning, portAvailable, readState, spawnServer, stateDirectory, waitForAuthenticatedUrl, waitForServer, writeState, } from "./standalone-server.js";
|
|
17
|
-
import { STANDALONE_PROFILE, applyProfileNpmPatches, ensureProfile, readDistributionProfile, resolvePaths, } from "./standalone-profile.js";
|
|
17
|
+
import { STANDALONE_PROFILE, applyProfileNpmPatches, ensureProfile, pnpmAvailable, pnpmInstallCommand, pnpmInstallCommands, readDistributionProfile, resolvePaths, } from "./standalone-profile.js";
|
|
18
18
|
/** Milliseconds a start waits for the server to answer before reporting failure. */
|
|
19
19
|
const READY_TIMEOUT_MILLISECONDS = 90_000;
|
|
20
20
|
function parseStartOptions(argv) {
|
|
@@ -100,6 +100,36 @@ async function start(argv) {
|
|
|
100
100
|
const options = parseStartOptions(argv);
|
|
101
101
|
const anchor = installationAnchor();
|
|
102
102
|
const paths = resolvePaths(anchor);
|
|
103
|
+
// The profile installs its own dependency tree, which needs pnpm. Asking here rather
|
|
104
|
+
// than failing inside the install turns a missing prerequisite into a decision the
|
|
105
|
+
// consumer makes, and the install it can run is the one command that provides it.
|
|
106
|
+
if (!pnpmAvailable()) {
|
|
107
|
+
const command = pnpmInstallCommand();
|
|
108
|
+
console.log('pnpm is required to install the ' + STANDALONE_PROFILE + ' profile, and was not found.');
|
|
109
|
+
console.log('Install it with: ' + command);
|
|
110
|
+
if (!await confirm('Install pnpm now?')) {
|
|
111
|
+
console.log('Install pnpm and run dsh-plus start again.');
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
// Try each installer in turn: Corepack is absent on an installation that disabled it,
|
|
115
|
+
// and npm succeeds there. A failure reports the commands rather than a stack trace.
|
|
116
|
+
let installed = false;
|
|
117
|
+
for (const candidate of pnpmInstallCommands()) {
|
|
118
|
+
const attempt = spawnSync(candidate, { stdio: 'inherit', shell: true });
|
|
119
|
+
if (attempt.status === 0 && pnpmAvailable()) {
|
|
120
|
+
installed = true;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (!installed) {
|
|
125
|
+
console.log('Could not install pnpm automatically.');
|
|
126
|
+
console.log('Run one of these, then run dsh-plus start again:');
|
|
127
|
+
for (const candidate of pnpmInstallCommands())
|
|
128
|
+
console.log(' ' + candidate);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
console.log('pnpm installed.');
|
|
132
|
+
}
|
|
103
133
|
const created = ensureProfile(paths, installationRoot());
|
|
104
134
|
console.log(created
|
|
105
135
|
? 'Created the ' + STANDALONE_PROFILE + ' profile at ' + paths.profileDirectory
|
|
@@ -94,6 +94,32 @@ export declare function installProfilePackages(paths: StandalonePaths, consumerD
|
|
|
94
94
|
* @param profileModules - the profile's \`node_modules\` directory.
|
|
95
95
|
*/
|
|
96
96
|
export declare function alignReplacedPackageNames(profileModules: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Report whether pnpm can run.
|
|
99
|
+
*
|
|
100
|
+
* The profile installs its own tree so its \`overrides\` apply, and only pnpm reads those
|
|
101
|
+
* from a workspace, so pnpm is a prerequisite the distribution cannot supply. Probing
|
|
102
|
+
* first turns a cryptic failure from the install into a message naming what is missing.
|
|
103
|
+
*
|
|
104
|
+
* @returns \`true\` when pnpm answers with a version.
|
|
105
|
+
*/
|
|
106
|
+
export declare function pnpmAvailable(): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* The commands that install pnpm, in the order worth trying.
|
|
109
|
+
*
|
|
110
|
+
* Corepack ships with Node and needs no download, so it comes first; npm is the fallback
|
|
111
|
+
* for an installation whose Corepack is absent or disabled. Both are the consumer's own
|
|
112
|
+
* toolchain, which is what lets the first start offer to install rather than only report.
|
|
113
|
+
*
|
|
114
|
+
* @returns commands to try in order, stopping at the first that works.
|
|
115
|
+
*/
|
|
116
|
+
export declare function pnpmInstallCommands(): readonly string[];
|
|
117
|
+
/**
|
|
118
|
+
* The command to show a consumer who declines the automatic install.
|
|
119
|
+
*
|
|
120
|
+
* @returns the preferred command, which is the one the offer runs first.
|
|
121
|
+
*/
|
|
122
|
+
export declare function pnpmInstallCommand(): string;
|
|
97
123
|
/** Resolve every path a command needs, without creating anything. */
|
|
98
124
|
export declare function resolvePaths(anchor: string, env?: NodeJS.ProcessEnv): StandalonePaths;
|
|
99
125
|
/**
|
|
@@ -197,15 +197,65 @@ export function alignReplacedPackageNames(profileModules) {
|
|
|
197
197
|
renameSync(temporary, manifestPath);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Run pnpm in one directory, inheriting its output.
|
|
202
|
+
*
|
|
203
|
+
* Windows resolves a command through its shell: \`spawnSync\` on a \`.cmd\` shim fails
|
|
204
|
+
* with EINVAL, so the call goes through \`cmd.exe\` there. This matches the runner in
|
|
205
|
+
* \`apply.ts\`, which the apply path has used on Windows since it shipped.
|
|
206
|
+
*
|
|
207
|
+
* @param cwd - the directory pnpm runs in.
|
|
208
|
+
* @param args - pnpm arguments, without the executable.
|
|
209
|
+
* @param label - the failing step's name, for the error.
|
|
210
|
+
*/
|
|
201
211
|
function runPnpm(cwd, args, label) {
|
|
202
|
-
const
|
|
203
|
-
|
|
212
|
+
const result = spawnSync('pnpm', [...args], {
|
|
213
|
+
cwd,
|
|
214
|
+
stdio: 'inherit',
|
|
215
|
+
shell: process.platform === 'win32',
|
|
216
|
+
});
|
|
204
217
|
if (result.error !== undefined)
|
|
205
218
|
throw result.error;
|
|
206
219
|
if (result.status !== 0)
|
|
207
220
|
throw new Error(label + ' failed with exit code ' + String(result.status));
|
|
208
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Report whether pnpm can run.
|
|
224
|
+
*
|
|
225
|
+
* The profile installs its own tree so its \`overrides\` apply, and only pnpm reads those
|
|
226
|
+
* from a workspace, so pnpm is a prerequisite the distribution cannot supply. Probing
|
|
227
|
+
* first turns a cryptic failure from the install into a message naming what is missing.
|
|
228
|
+
*
|
|
229
|
+
* @returns \`true\` when pnpm answers with a version.
|
|
230
|
+
*/
|
|
231
|
+
export function pnpmAvailable() {
|
|
232
|
+
const probe = spawnSync('pnpm', ['--version'], {
|
|
233
|
+
stdio: 'pipe',
|
|
234
|
+
shell: process.platform === 'win32',
|
|
235
|
+
encoding: 'utf8',
|
|
236
|
+
});
|
|
237
|
+
return probe.status === 0;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The commands that install pnpm, in the order worth trying.
|
|
241
|
+
*
|
|
242
|
+
* Corepack ships with Node and needs no download, so it comes first; npm is the fallback
|
|
243
|
+
* for an installation whose Corepack is absent or disabled. Both are the consumer's own
|
|
244
|
+
* toolchain, which is what lets the first start offer to install rather than only report.
|
|
245
|
+
*
|
|
246
|
+
* @returns commands to try in order, stopping at the first that works.
|
|
247
|
+
*/
|
|
248
|
+
export function pnpmInstallCommands() {
|
|
249
|
+
return ['corepack enable pnpm', 'npm install -g pnpm'];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The command to show a consumer who declines the automatic install.
|
|
253
|
+
*
|
|
254
|
+
* @returns the preferred command, which is the one the offer runs first.
|
|
255
|
+
*/
|
|
256
|
+
export function pnpmInstallCommand() {
|
|
257
|
+
return pnpmInstallCommands()[0] ?? 'npm install -g pnpm';
|
|
258
|
+
}
|
|
209
259
|
/**
|
|
210
260
|
* Expose the distribution's nested packages at the top level the profile searches.
|
|
211
261
|
*
|
package/package.json
CHANGED