@penvhq/cli 0.12.0 → 0.14.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.
@@ -130,6 +130,7 @@ function escapeArgument(argument, doubleEscape) {
130
130
  // src/install.ts
131
131
  var RUNTIME_PACKAGE = "@penvhq/penv";
132
132
  var SCHEMA_PACKAGE = "zod";
133
+ var TYPES_PACKAGE = "@penvhq/core";
133
134
  var LOCKFILES = [
134
135
  ["pnpm", "pnpm-lock.yaml"],
135
136
  ["yarn", "yarn.lock"],
@@ -232,6 +233,10 @@ function declaredIn(dir, name) {
232
233
  }
233
234
  return void 0;
234
235
  }
236
+ function blockPresentIn(dir, dev) {
237
+ const block = manifestOf(dir)?.[dev ? "devDependencies" : "dependencies"];
238
+ return block !== null && typeof block === "object" && !Array.isArray(block);
239
+ }
235
240
  function isPnpmWorkspaceRoot(root) {
236
241
  return existsSync2(join2(root, PNPM_WORKSPACE)) && existsSync2(join2(root, "package.json"));
237
242
  }
@@ -355,6 +360,8 @@ function stepFor(manager, manifest, packages, options) {
355
360
  manifest,
356
361
  packages,
357
362
  command: addCommand(manager, options, specs),
363
+ dev: options.dev,
364
+ blockPresent: blockPresentIn(options.dir, options.dev),
358
365
  satisfied: pending.length === 0
359
366
  };
360
367
  }
@@ -366,6 +373,7 @@ function planInstall(root, version = engineVersion()) {
366
373
  const workspaceRoot = manager === "pnpm" && isPnpmWorkspaceRoot(root);
367
374
  const runtime = declaredIn(root, RUNTIME_PACKAGE);
368
375
  const zod = declaredIn(root, SCHEMA_PACKAGE);
376
+ const types = declaredIn(root, TYPES_PACKAGE);
369
377
  const packages = [
370
378
  {
371
379
  name: RUNTIME_PACKAGE,
@@ -382,34 +390,47 @@ function planInstall(root, version = engineVersion()) {
382
390
  satisfied: zod !== void 0
383
391
  }
384
392
  ];
393
+ const typesPackage = {
394
+ name: TYPES_PACKAGE,
395
+ version,
396
+ ...types === void 0 ? {} : { declared: types.version },
397
+ // Any declared version counts, for zod's reason: the augmentation binds on
398
+ // the module resolving, not on which release of it a project pinned.
399
+ satisfied: types !== void 0
400
+ };
385
401
  const pending = packages.filter((entry) => !entry.satisfied);
386
402
  const steps = [
387
403
  stepFor(manager, "package.json", packages, {
388
404
  workspaceRoot,
405
+ dir: root,
389
406
  dev: runtime?.dev === true && pending.every((entry) => entry.name === RUNTIME_PACKAGE) && pending.length > 0
390
- })
407
+ }),
408
+ stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dir: root, dev: true })
391
409
  ];
392
- for (const dir of workspaceMembers(root, RUNTIME_PACKAGE)) {
393
- const declared = declaredIn(dir, RUNTIME_PACKAGE);
394
- steps.push(
395
- stepFor(
396
- manager,
397
- manifestPathOf(root, dir),
398
- [
410
+ for (const name of [RUNTIME_PACKAGE, TYPES_PACKAGE]) {
411
+ for (const dir of workspaceMembers(root, name)) {
412
+ const declared = declaredIn(dir, name);
413
+ steps.push(
414
+ stepFor(
415
+ manager,
416
+ manifestPathOf(root, dir),
417
+ [
418
+ {
419
+ name,
420
+ version,
421
+ declared: declared.version,
422
+ satisfied: declared.version === version
423
+ }
424
+ ],
399
425
  {
400
- name: RUNTIME_PACKAGE,
401
- version,
402
- declared: declared.version,
403
- satisfied: declared.version === version
426
+ filter: `./${relative(root, dir).split(sep).join("/")}`,
427
+ workspaceRoot: false,
428
+ dir,
429
+ dev: declared.dev
404
430
  }
405
- ],
406
- {
407
- filter: `./${relative(root, dir).split(sep).join("/")}`,
408
- workspaceRoot: false,
409
- dev: declared.dev
410
- }
411
- )
412
- );
431
+ )
432
+ );
433
+ }
413
434
  }
414
435
  return {
415
436
  root,
@@ -426,16 +447,39 @@ function installedPackages(plan) {
426
447
  const pending = plan.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);
427
448
  return [...new Map(pending.map((entry) => [entry.name, entry])).values()];
428
449
  }
450
+ function installedByManifest(plan) {
451
+ const byManifest = /* @__PURE__ */ new Map();
452
+ for (const step of plan.steps.filter((entry) => !entry.satisfied)) {
453
+ const landed = byManifest.get(step.manifest) ?? [];
454
+ landed.push(...step.packages.filter((entry) => !entry.satisfied));
455
+ byManifest.set(step.manifest, landed);
456
+ }
457
+ return [...byManifest].map(([manifest, packages]) => ({
458
+ manifest,
459
+ packages: series(
460
+ [...new Map(packages.map((entry) => [entry.name, entry])).values()].map(describe)
461
+ )
462
+ }));
463
+ }
464
+ function plannedPackages(plan) {
465
+ const all = plan.steps.flatMap((step) => step.packages);
466
+ return [...new Map(all.map((entry) => [entry.name, entry])).values()];
467
+ }
468
+ function series(values) {
469
+ return values.length < 2 ? values[0] ?? "" : `${values.slice(0, -1).join(", ")} and ${values.at(-1)}`;
470
+ }
429
471
  function renderStep(step) {
430
472
  const pending = step.packages.filter((entry) => !entry.satisfied);
431
473
  const added = pending.filter((entry) => entry.declared === void 0);
432
474
  const replaced = pending.filter((entry) => entry.declared !== void 0);
475
+ const block = step.dev ? "devDependencies" : "dependencies";
476
+ const edge = step.blockPresent ? " " : " +";
433
477
  return [
434
478
  step.manifest,
435
479
  ...added.length === 0 ? [] : [
436
- ' + "dependencies": {',
480
+ `${edge} "${block}": {`,
437
481
  ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
438
- " + }"
482
+ `${edge} }`
439
483
  ],
440
484
  ...replaced.flatMap((entry) => [
441
485
  ` - "${entry.name}": "${entry.declared}"`,
@@ -445,9 +489,8 @@ function renderStep(step) {
445
489
  }
446
490
  function renderInstallPlan(plan) {
447
491
  if (plan.satisfied) {
448
- const packages = plan.steps[0]?.packages ?? [];
449
492
  return [
450
- `package.json already has ${packages.map(describe).join(" and ")} \u2014 nothing to install.`
493
+ `package.json already has ${series(plannedPackages(plan).map(describe))} \u2014 nothing to install.`
451
494
  ];
452
495
  }
453
496
  const pending = plan.steps.filter((step) => !step.satisfied);
@@ -491,14 +534,16 @@ export {
491
534
  noCommand,
492
535
  RUNTIME_PACKAGE,
493
536
  SCHEMA_PACKAGE,
537
+ TYPES_PACKAGE,
494
538
  engineVersion,
495
539
  schemaPackageVersion,
496
540
  detectPackageManager,
497
541
  isPnpmWorkspaceRoot,
498
542
  planInstall,
499
543
  installedPackages,
544
+ installedByManifest,
500
545
  renderInstallPlan,
501
546
  installWithPackageManager,
502
547
  installFailed
503
548
  };
504
- //# sourceMappingURL=chunk-NHTDIZZ2.js.map
549
+ //# sourceMappingURL=chunk-4RR6UFVZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/install.ts","../src/child.ts"],"sourcesContent":["/**\n * The runtime dependencies an adopted project takes, and how they get there.\n *\n * PRD §3: an adopted project depends on `@penvhq/penv` at the engine's own\n * version — the typed `@env` surface, not a CLI distribution. It also depends on\n * zod, because the `penv.schema.ts` init scaffolds imports it: zod is a *peer* of\n * `@penvhq/penv`, and a peer is a package the project supplies. Under pnpm's\n * strict layout nothing hoists it to the project root, so an install that named\n * only `@penvhq/penv` left the very schema init had just written unable to\n * resolve `zod` — and adoption could never finish.\n *\n * Both are installed with the package manager the project already uses, and only\n * after showing the exact `package.json` and lockfile change: an install is the\n * one step of adoption that reaches outside the repository, so it is the one step\n * that is shown before it happens rather than reported after.\n *\n * The install itself is a seam. It shells out to a package manager, which the\n * tests must never do — and a fake here is not a weaker test, because what init\n * has to get right is the plan, the consent, and the refusal when the install\n * does not happen.\n *\n * A plan is a list of steps, because in a workspace \"the project's dependency\"\n * is plural. pnpm refuses a bare `add` at a workspace root (`-w` is how you say\n * you meant the root), and a workspace package that declares `@penvhq/penv`\n * itself is a second copy of the very version the manifest pins — one repository\n * ran the 0.8 bridge under a 0.11 pin for three releases because nothing looked\n * below the root, and the same repository then held two `@penvhq/core`s five\n * minor versions apart because the scan looked for only one name. So every\n * `package.json` that declares either moves, under one consent, and the commands\n * shown are the ones that run.\n *\n * Two commands write that dependency line: `penv init`, which is the engine's,\n * and `penv upgrade`, which is the launcher's. This module is published at\n * `@penvhq/cli/install` so the launcher reaches it without loading the command\n * surface — one answer to \"which package manager, which diff, which spawn\",\n * rather than a second copy on the other side of the launcher/engine split.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\nimport { startChild } from \"./child.js\";\n\n/** The package an adopted project depends on. The CLI engine is not one of its dependencies. */\nexport const RUNTIME_PACKAGE = \"@penvhq/penv\";\n\n/** The peer `penv.schema.ts` imports, which the project supplies because a peer is not hoisted. */\nexport const SCHEMA_PACKAGE = \"zod\";\n\n/**\n * The module every committed provider declaration augments, which the project\n * declares for the same reason it declares zod: nothing hoists it.\n *\n * `penv add` commits a `declare module \"@penvhq/core\"` block, and TypeScript\n * resolves that specifier from the project's own files. Under pnpm's strict\n * layout a transitive dependency is not at the project root, so the specifier\n * resolves to nothing — and an augmentation whose module cannot be found is not\n * an error, it silently degrades to an *ambient* declaration. The map\n * `defineConfig` reads stays empty, a misspelled provider field compiles clean,\n * and no diagnostic anywhere says so. A hoisting package manager hides this; the\n * project declaring it is what makes the declaration bind under all of them.\n *\n * `devDependencies`, because that is the whole of what it is: a type-only\n * augmentation target. No application code imports it, and `@penvhq/penv`\n * carries its own copy of the runtime — so the PRD's one runtime dependency is\n * still one.\n */\nexport const TYPES_PACKAGE = \"@penvhq/core\";\n\nexport type PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\n/** The lockfile that names each manager, checked in this order. */\nconst LOCKFILES: readonly (readonly [PackageManager, string])[] = [\n [\"pnpm\", \"pnpm-lock.yaml\"],\n [\"yarn\", \"yarn.lock\"],\n [\"bun\", \"bun.lock\"],\n [\"bun\", \"bun.lockb\"],\n [\"npm\", \"package-lock.json\"],\n];\n\n/** How each manager is told to add a package. */\nconst ADD: Readonly<Record<PackageManager, readonly [string, string]>> = {\n pnpm: [\"pnpm\", \"add\"],\n npm: [\"npm\", \"install\"],\n yarn: [\"yarn\", \"add\"],\n bun: [\"bun\", \"add\"],\n};\n\n/** How each manager is told to write the version down exactly, with no range. */\nconst EXACT: Readonly<Record<PackageManager, string>> = {\n pnpm: \"--save-exact\",\n npm: \"--save-exact\",\n yarn: \"--exact\",\n bun: \"--exact\",\n};\n\n/** How each manager is told to keep the dependency in the block it is already in. */\nconst DEV: Readonly<Record<PackageManager, string>> = {\n pnpm: \"-D\",\n npm: \"--save-dev\",\n yarn: \"--dev\",\n bun: \"--dev\",\n};\n\n/** pnpm refuses an install at a workspace root without this — `ERR_PNPM_ADDING_TO_ROOT`. */\nconst WORKSPACE_ROOT_FLAG = \"-w\";\n\n/** pnpm's workspace file, which is both what declares the members and what makes the root refuse. */\nconst PNPM_WORKSPACE = \"pnpm-workspace.yaml\";\n\n/** One package the adopted project needs, and what its `package.json` says today. */\nexport interface InstallPackage {\n readonly name: string;\n readonly version: string;\n /** What `package.json` already says about it, when it says anything. */\n readonly declared?: string;\n /** True when this project already has it — nothing to install for this one. */\n readonly satisfied: boolean;\n}\n\n/** One `package.json` the install rewrites, and the command that rewrites it. */\nexport interface InstallStep {\n /** The file the diff names — `package.json`, or a workspace package's path to it. */\n readonly manifest: string;\n /** Everything this file needs, in the order the diff shows them. */\n readonly packages: readonly InstallPackage[];\n /** The command, argv-shaped — run from the project root, whichever file it writes. */\n readonly command: readonly string[];\n /** The block this step writes into, which is what the diff shows. */\n readonly dev: boolean;\n /**\n * True when that block is already in the file. The diff then shows it as the\n * context it is, without a `+`: claiming to create a `devDependencies` beside\n * fourteen entries is the one line in an otherwise literal diff that a reader\n * would go and check by hand.\n */\n readonly blockPresent: boolean;\n /** True when this file already declares every one of them. */\n readonly satisfied: boolean;\n}\n\nexport interface InstallPlan {\n readonly root: string;\n readonly manager: PackageManager;\n /**\n * The root's `package.json` — one step per block it writes, since one command\n * names one — then one per workspace package that declares the runtime package\n * or the types package itself.\n */\n readonly steps: readonly InstallStep[];\n /** The lockfile the manager will rewrite, when the project has one. */\n readonly lockfile?: string;\n /** True when every step is already satisfied — nothing to install. */\n readonly satisfied: boolean;\n}\n\n/** Runs an install plan, or throws. Replaced in tests; never spawns there. */\nexport type InstallRuntime = (plan: InstallPlan) => Promise<void>;\n\n/**\n * The engine's own version, read from its manifest rather than restated in the\n * source: `@penvhq/penv` must match the engine exactly, and a constant beside\n * the version a release bumps is a second answer waiting to drift.\n */\nexport function engineVersion(): string {\n const version = ownManifest()?.version;\n if (typeof version === \"string\" && version.length > 0) {\n return version;\n }\n throw new PenvError(\n \"ENGINE_VERSION_UNREADABLE\",\n \"penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs\",\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/**\n * The zod an adopted project installs: the floor of the peer range the engine\n * and `@penvhq/penv` both declare, which is the version penv is built and tested\n * against.\n *\n * The floor rather than the range, because the diff shown before the install has\n * to be the line that actually lands — `--save-exact` on `^4.4.3` would write\n * whatever the registry resolved that day, which is not something a reader can\n * consent to in advance.\n */\nexport function schemaPackageVersion(): string {\n const peers = ownManifest()?.peerDependencies;\n const declared =\n peers !== null && typeof peers === \"object\" && !Array.isArray(peers)\n ? (peers as Record<string, unknown>)[SCHEMA_PACKAGE]\n : undefined;\n const floor = typeof declared === \"string\" ? declared.replace(/^[\\^~>=\\s]+/, \"\").trim() : \"\";\n if (floor.length > 0) {\n return floor;\n }\n throw new PenvError(\n \"ENGINE_PEER_UNREADABLE\",\n `penv could not read its own \\`${SCHEMA_PACKAGE}\\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,\n `Reinstall penv, then run \\`penv init\\` again.`,\n );\n}\n\n/** The engine's own manifest, or `undefined` when it cannot be read. */\nfunction ownManifest(): Record<string, unknown> | undefined {\n try {\n const parsed: unknown = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf8\"),\n );\n return parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : undefined;\n } catch {\n // The callers refuse: a version penv guessed would pin a project's\n // dependency to something nobody chose.\n return undefined;\n }\n}\n\n/** The package manager this project already uses: its lockfile, then what it declares, then npm. */\nexport function detectPackageManager(root: string): PackageManager {\n for (const [manager, lockfile] of LOCKFILES) {\n if (existsSync(join(root, lockfile))) {\n return manager;\n }\n }\n return declaredManager(root) ?? \"npm\";\n}\n\n/** `\"packageManager\": \"pnpm@9.1.0\"` — corepack's field, and a project's own answer. */\nfunction declaredManager(root: string): PackageManager | undefined {\n const declared = manifestOf(root)?.packageManager;\n if (typeof declared !== \"string\") {\n return undefined;\n }\n const name = declared.split(\"@\")[0];\n return name === \"pnpm\" || name === \"npm\" || name === \"yarn\" || name === \"bun\" ? name : undefined;\n}\n\nfunction manifestOf(root: string): Record<string, unknown> | undefined {\n const file = join(root, \"package.json\");\n if (!existsSync(file)) {\n return undefined;\n }\n try {\n const manifest: unknown = JSON.parse(readFileSync(file, \"utf8\"));\n return manifest !== null && typeof manifest === \"object\" && !Array.isArray(manifest)\n ? (manifest as Record<string, unknown>)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** What one `package.json` says about a package today, and which block says it. */\ninterface Declaration {\n readonly version: string;\n /** True when it sits in `devDependencies` — where an install has to leave it. */\n readonly dev: boolean;\n}\n\nfunction declaredIn(dir: string, name: string): Declaration | undefined {\n const manifest = manifestOf(dir);\n for (const field of [\"dependencies\", \"devDependencies\"] as const) {\n const block: unknown = manifest?.[field];\n if (block !== null && typeof block === \"object\" && !Array.isArray(block)) {\n const version: unknown = (block as Record<string, unknown>)[name];\n if (typeof version === \"string\") {\n return { version, dev: field === \"devDependencies\" };\n }\n }\n }\n return undefined;\n}\n\n/** True when `package.json` already has the block a step writes into. */\nfunction blockPresentIn(dir: string, dev: boolean): boolean {\n const block: unknown = manifestOf(dir)?.[dev ? \"devDependencies\" : \"dependencies\"];\n return block !== null && typeof block === \"object\" && !Array.isArray(block);\n}\n\n/** True when `root` is the root of a pnpm workspace, which is what `-w` is for. */\nexport function isPnpmWorkspaceRoot(root: string): boolean {\n return existsSync(join(root, PNPM_WORKSPACE)) && existsSync(join(root, \"package.json\"));\n}\n\n/** The `packages:` list from `pnpm-workspace.yaml`, block or flow form. */\nfunction workspaceGlobs(root: string): string[] {\n let text: string;\n try {\n text = readFileSync(join(root, PNPM_WORKSPACE), \"utf8\");\n } catch {\n return [];\n }\n const unquote = (raw: string): string => raw.replace(/^['\"]|['\"]$/g, \"\").trim();\n const globs: string[] = [];\n let inside = false;\n for (const line of text.split(/\\r?\\n/)) {\n const flow = /^packages:\\s*\\[(.*)\\]\\s*$/.exec(line);\n if (flow?.[1] !== undefined) {\n return flow[1]\n .split(\",\")\n .map(unquote)\n .filter((glob) => glob !== \"\");\n }\n if (/^packages:\\s*$/.test(line)) {\n inside = true;\n continue;\n }\n if (!inside) {\n continue;\n }\n const item = /^\\s+-\\s*(.+?)\\s*$/.exec(line);\n if (item?.[1] !== undefined) {\n globs.push(unquote(item[1]));\n continue;\n }\n if (line.trim() !== \"\" && !line.trimStart().startsWith(\"#\")) {\n break;\n }\n }\n return globs;\n}\n\nfunction directoriesIn(dir: string): string[] {\n try {\n return readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && entry.name !== \"node_modules\")\n .map((entry) => join(dir, entry.name));\n } catch {\n return [];\n }\n}\n\nfunction isDirectory(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** `packages/*` and `apps/**` against the filesystem, one path segment at a time. */\nfunction expandGlob(root: string, glob: string): string[] {\n let dirs = [root];\n for (const segment of glob.split(\"/\").filter((part) => part !== \"\" && part !== \".\")) {\n const next: string[] = [];\n for (const dir of dirs) {\n if (segment === \"**\") {\n const stack = [dir];\n while (stack.length > 0) {\n const current = stack.pop() as string;\n next.push(current);\n stack.push(...directoriesIn(current));\n }\n continue;\n }\n if (segment.includes(\"*\")) {\n const pattern = new RegExp(\n `^${segment.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \"[^/]*\")}$`,\n );\n next.push(\n ...directoriesIn(dir).filter((child) => pattern.test(child.slice(dir.length + 1))),\n );\n continue;\n }\n const candidate = join(dir, segment);\n if (isDirectory(candidate)) {\n next.push(candidate);\n }\n }\n dirs = next;\n }\n return dirs;\n}\n\n/**\n * Every workspace package that declares `name` itself, root excluded — asked\n * once for `@penvhq/penv` and once for `@penvhq/core`, since a member holding\n * its own copy of either is a second version running under one pin.\n *\n * Only the ones that already declare it: penv moves a dependency a package\n * chose, and adding one to a package that never asked for it is a different\n * decision than the one being consented to.\n */\nfunction workspaceMembers(root: string, name: string): string[] {\n if (!isPnpmWorkspaceRoot(root)) {\n return [];\n }\n const globs = workspaceGlobs(root);\n const excluded = globs\n .filter((glob) => glob.startsWith(\"!\"))\n .flatMap((glob) => expandGlob(root, glob.slice(1)));\n const found = new Set<string>();\n for (const glob of globs.filter((entry) => !entry.startsWith(\"!\"))) {\n for (const dir of expandGlob(root, glob)) {\n if (dir !== root && !excluded.includes(dir) && declaredIn(dir, name) !== undefined) {\n found.add(dir);\n }\n }\n }\n return [...found].sort();\n}\n\n/** The path a diff shows for one of them, in the spelling every penv path uses. */\nfunction manifestPathOf(root: string, dir: string): string {\n const within = relative(root, dir)\n .split(sep)\n .filter((part) => part !== \"\");\n return [...within, \"package.json\"].join(\"/\");\n}\n\nfunction addCommand(\n manager: PackageManager,\n options: { readonly filter?: string; readonly workspaceRoot: boolean; readonly dev: boolean },\n specs: readonly string[],\n): string[] {\n const [bin, verb] = ADD[manager];\n return [\n bin,\n ...(options.filter === undefined ? [] : [\"--filter\", options.filter]),\n verb,\n ...(options.workspaceRoot ? [WORKSPACE_ROOT_FLAG] : []),\n EXACT[manager],\n ...(options.dev ? [DEV[manager]] : []),\n ...specs,\n ];\n}\n\nfunction stepFor(\n manager: PackageManager,\n manifest: string,\n packages: readonly InstallPackage[],\n options: {\n readonly filter?: string;\n readonly workspaceRoot: boolean;\n readonly dev: boolean;\n readonly dir: string;\n },\n): InstallStep {\n const pending = packages.filter((entry) => !entry.satisfied);\n const specs = (pending.length === 0 ? packages : pending).map(\n (entry) => `${entry.name}@${entry.version}`,\n );\n return {\n manifest,\n packages,\n command: addCommand(manager, options, specs),\n dev: options.dev,\n blockPresent: blockPresentIn(options.dir, options.dev),\n satisfied: pending.length === 0,\n };\n}\n\nexport function planInstall(root: string, version: string = engineVersion()): InstallPlan {\n const manager = detectPackageManager(root);\n const lockfile = LOCKFILES.find(\n ([name, file]) => name === manager && existsSync(join(root, file)),\n )?.[1];\n const workspaceRoot = manager === \"pnpm\" && isPnpmWorkspaceRoot(root);\n\n const runtime = declaredIn(root, RUNTIME_PACKAGE);\n const zod = declaredIn(root, SCHEMA_PACKAGE);\n const types = declaredIn(root, TYPES_PACKAGE);\n const packages: InstallPackage[] = [\n {\n name: RUNTIME_PACKAGE,\n version,\n ...(runtime === undefined ? {} : { declared: runtime.version }),\n satisfied: runtime?.version === version,\n },\n {\n name: SCHEMA_PACKAGE,\n version: schemaPackageVersion(),\n ...(zod === undefined ? {} : { declared: zod.version }),\n // Any declared zod counts: which zod a project uses is the project's\n // decision, and penv is here to make sure there is one, not to move it.\n satisfied: zod !== undefined,\n },\n ];\n const typesPackage: InstallPackage = {\n name: TYPES_PACKAGE,\n version,\n ...(types === undefined ? {} : { declared: types.version }),\n // Any declared version counts, for zod's reason: the augmentation binds on\n // the module resolving, not on which release of it a project pinned.\n satisfied: types !== undefined,\n };\n\n // The block a package chose is the block penv writes back to — but only when\n // every package this step installs lives there, since one command names one.\n // Which is also why the types package is its own step: it belongs in\n // `devDependencies` whatever the other two chose.\n const pending = packages.filter((entry) => !entry.satisfied);\n const steps: InstallStep[] = [\n stepFor(manager, \"package.json\", packages, {\n workspaceRoot,\n dir: root,\n dev:\n runtime?.dev === true &&\n pending.every((entry) => entry.name === RUNTIME_PACKAGE) &&\n pending.length > 0,\n }),\n stepFor(manager, \"package.json\", [typesPackage], { workspaceRoot, dir: root, dev: true }),\n ];\n // Both packages, because a member declaring either holds its own copy of it:\n // `@penvhq/penv` is a second bridge running under the pin, and `@penvhq/core`\n // is a second copy of the interfaces every committed declaration augments.\n for (const name of [RUNTIME_PACKAGE, TYPES_PACKAGE]) {\n for (const dir of workspaceMembers(root, name)) {\n const declared = declaredIn(dir, name) as Declaration;\n steps.push(\n stepFor(\n manager,\n manifestPathOf(root, dir),\n [\n {\n name,\n version,\n declared: declared.version,\n satisfied: declared.version === version,\n },\n ],\n {\n filter: `./${relative(root, dir).split(sep).join(\"/\")}`,\n workspaceRoot: false,\n dir,\n dev: declared.dev,\n },\n ),\n );\n }\n }\n\n return {\n root,\n manager,\n steps,\n ...(lockfile === undefined ? {} : { lockfile }),\n satisfied: steps.every((step) => step.satisfied),\n };\n}\n\nfunction describe(entry: InstallPackage): string {\n return `${entry.name} ${entry.version}`;\n}\n\n/** What this plan actually installs, once per package however many files declare it. */\nexport function installedPackages(plan: InstallPlan): readonly InstallPackage[] {\n const pending = plan.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);\n return [...new Map(pending.map((entry) => [entry.name, entry])).values()];\n}\n\n/** One file the plan wrote, and what landed in it. */\nexport interface InstalledManifest {\n readonly manifest: string;\n /** `@penvhq/penv 1.2.3 and @penvhq/core 1.2.3` — every package this file gained. */\n readonly packages: string;\n}\n\n/**\n * What the install landed, one entry per `package.json`.\n *\n * Per file rather than per step, because the root is two steps and a file is\n * what the reader recognises — and every package in it, because a caller\n * confirming only the one it happened to name leaves the release's own new\n * dependency unmentioned in the lines that say the upgrade worked.\n */\nexport function installedByManifest(plan: InstallPlan): readonly InstalledManifest[] {\n const byManifest = new Map<string, InstallPackage[]>();\n for (const step of plan.steps.filter((entry) => !entry.satisfied)) {\n const landed = byManifest.get(step.manifest) ?? [];\n landed.push(...step.packages.filter((entry) => !entry.satisfied));\n byManifest.set(step.manifest, landed);\n }\n return [...byManifest].map(([manifest, packages]) => ({\n manifest,\n packages: series(\n [...new Map(packages.map((entry) => [entry.name, entry])).values()].map(describe),\n ),\n }));\n}\n\n/** Every package the plan names, once each — the root's blocks are two steps now. */\nfunction plannedPackages(plan: InstallPlan): readonly InstallPackage[] {\n const all = plan.steps.flatMap((step) => step.packages);\n return [...new Map(all.map((entry) => [entry.name, entry])).values()];\n}\n\n/** `a`, `a and b`, `a, b and c`. */\nfunction series(values: readonly string[]): string {\n return values.length < 2\n ? (values[0] ?? \"\")\n : `${values.slice(0, -1).join(\", \")} and ${values.at(-1) as string}`;\n}\n\n/** The lines one `package.json` contributes to the diff. */\nfunction renderStep(step: InstallStep): string[] {\n const pending = step.packages.filter((entry) => !entry.satisfied);\n const added = pending.filter((entry) => entry.declared === undefined);\n const replaced = pending.filter((entry) => entry.declared !== undefined);\n const block = step.dev ? \"devDependencies\" : \"dependencies\";\n // The block's own lines are an insertion only when the block is not there yet.\n const edge = step.blockPresent ? \" \" : \" +\";\n return [\n step.manifest,\n ...(added.length === 0\n ? []\n : [\n `${edge} \"${block}\": {`,\n ...added.map((entry) => ` + \"${entry.name}\": \"${entry.version}\"`),\n `${edge} }`,\n ]),\n ...replaced.flatMap((entry) => [\n ` - \"${entry.name}\": \"${entry.declared}\"`,\n ` + \"${entry.name}\": \"${entry.version}\"`,\n ]),\n ];\n}\n\n/**\n * The change, as it will appear in the diff — the whole point of showing it is\n * that the reader recognises their own file, so these are the `package.json`\n * lines that land and the lockfile that gets rewritten, not a summary of both.\n *\n * In a workspace that is more than one file, and the commands underneath are the\n * ones that run: a \"Run with:\" line the reader cannot paste is worse than none.\n */\nexport function renderInstallPlan(plan: InstallPlan): string[] {\n if (plan.satisfied) {\n return [\n `package.json already has ${series(plannedPackages(plan).map(describe))} — nothing to install.`,\n ];\n }\n const pending = plan.steps.filter((step) => !step.satisfied);\n const [first, ...rest] = pending.map((step) => step.command.join(\" \"));\n const landing = installedPackages(plan).map((entry) => ` + ${entry.name}@${entry.version}`);\n return [\n ...pending.flatMap(renderStep),\n ...(plan.lockfile === undefined ? [] : [plan.lockfile, ...landing]),\n \"\",\n `Run with: ${first ?? \"\"}`,\n ...rest.map((command) => ` then ${command}`),\n ];\n}\n\n/**\n * The real install: the project's own package manager, started the way any other\n * child is (`.cmd` shims on Windows included), with its output the user's to see.\n *\n * Every step runs from the project root — `-w` and `--filter` are how a workspace\n * says which `package.json` it means, so the directory never changes.\n */\nexport const installWithPackageManager: InstallRuntime = async (plan) => {\n for (const step of plan.steps) {\n if (step.satisfied) {\n continue;\n }\n const child = startChild({\n command: step.command,\n env: process.env as Record<string, string>,\n cwd: plan.root,\n purpose: `install ${step.packages.map(describe).join(\" and \")} in ${step.manifest}`,\n });\n const ended = await child.ended;\n if (ended.exitCode !== 0 || ended.signal !== null) {\n throw installFailed(plan, step);\n }\n }\n};\n\n/**\n * What to do about a package manager that refused.\n *\n * Never the command that just failed: the one remediation guaranteed not to work\n * is the one the reader already ran. The manager said why, on their screen, and\n * nothing was migrated — so the answer is that line and a second `penv init`.\n */\nexport function installFailed(plan: InstallPlan, step: InstallStep): PenvError {\n return new PenvError(\n \"INIT_INSTALL_FAILED\",\n `${step.command.join(\" \")} did not finish, so penv migrated nothing`,\n `Read what ${plan.manager} printed above — it names what it refused. Fix that and run this ` +\n \"command again; your dotenv files are exactly where they were.\",\n );\n}\n","/**\n * Starting someone else's command, opaquely.\n *\n * `penv run -- <command>` starts exactly what follows `--`: the argument\n * boundaries the shell already worked out are handed to the operating system\n * untouched, stdio is the parent's, and the child's exit code and terminating\n * signal come back out. penv never parses the command, never rebuilds a command\n * line from it, never wraps it in a shell — a shell would re-split what the user\n * already split, and `penv run -- node -e \"console.log(1 > 2)\"` would redirect to\n * a file called `2`.\n *\n * Windows is the one place where \"hand it to the operating system\" needs help.\n * `pnpm`, `next` and every other node-installed tool are `.cmd` shims there, and\n * Node refuses to execute one without a shell. So a `.cmd`/`.bat` target — and\n * only that — is started through `cmd.exe /d /s /c` with\n * `windowsVerbatimArguments`, building the one command line cmd will accept and\n * escaping every argument so that cmd hands the child the same bytes penv was\n * given. Everything else spawns directly, on every platform.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { delimiter, isAbsolute, join, win32 } from \"node:path\";\nimport { PenvError } from \"@penvhq/core\";\n\n/** How a child ended. Exactly one of these is meaningful, and both are forwarded. */\nexport interface ChildResult {\n /** The child's own exit code, or 1 when a signal ended it. */\n readonly exitCode: number;\n /** The signal that ended the child, when one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\nexport interface ChildInvocation {\n /** The command exactly as it followed `--`: the executable, then its arguments. */\n readonly command: readonly string[];\n readonly env: Record<string, string>;\n readonly cwd: string;\n /**\n * What penv is starting this on its own behalf to do — `init`'s dependency\n * install. Absent means the command is the user's, from after `--`, and the\n * two failures have opposite remedies: one is about what they typed, the other\n * about a program penv chose to run.\n */\n readonly purpose?: string;\n}\n\n/** A started child: how it ends, and the one thing a wrapper may do to it. */\nexport interface ChildHandle {\n /** Resolves when the child has ended, however it ended. */\n readonly ended: Promise<ChildResult>;\n /** Asks the child to stop — what `--watch` does before it starts the next one. */\n kill(signal?: NodeJS.Signals): void;\n}\n\n/** The seam `run` starts a child through — replaced in tests that assert what it was given. */\nexport type StartChild = (invocation: ChildInvocation) => ChildHandle;\n\n/** The signals a wrapper must pass through rather than absorb. */\nconst FORWARDED: readonly NodeJS.Signals[] = [\"SIGINT\", \"SIGTERM\", \"SIGHUP\", \"SIGBREAK\"];\n\nexport const startChild: StartChild = (invocation) => {\n const [executable, ...args] = invocation.command;\n if (executable === undefined) {\n throw noCommand();\n }\n\n const target = resolveTarget(executable, args, invocation.env);\n const child = spawn(target.file, target.args, {\n cwd: invocation.cwd,\n env: invocation.env,\n stdio: \"inherit\",\n ...(target.verbatim ? { windowsVerbatimArguments: true } : {}),\n });\n\n // Forwarded rather than handled: penv is a wrapper, and a Ctrl-C belongs to\n // the program the user is looking at. The child decides what to do with it,\n // and its answer comes back as the signal below.\n const forward = new Map<NodeJS.Signals, () => void>();\n for (const signal of FORWARDED) {\n const handler = (): void => {\n child.kill(signal);\n };\n forward.set(signal, handler);\n process.on(signal, handler);\n }\n const release = (): void => {\n for (const [signal, handler] of forward) {\n process.off(signal, handler);\n }\n };\n\n const ended = new Promise<ChildResult>((resolve, reject) => {\n child.on(\"error\", (cause) => {\n release();\n reject(cannotStart(executable, cause, invocation.purpose));\n });\n child.on(\"exit\", (code, signal) => {\n release();\n resolve({ exitCode: code ?? 1, signal });\n });\n });\n\n return {\n ended,\n kill(signal) {\n child.kill(signal);\n },\n };\n};\n\nexport function noCommand(): PenvError {\n return new PenvError(\n \"RUN_NO_COMMAND\",\n \"`penv run` was given no command to start\",\n \"Put the command after `--`, e.g. `penv run -- pnpm dev`.\",\n );\n}\n\nfunction cannotStart(executable: string, cause: unknown, purpose: string | undefined): PenvError {\n const detail = cause instanceof Error ? cause.message : String(cause);\n if (purpose !== undefined) {\n return new PenvError(\n \"PENV_COMMAND_NOT_STARTED\",\n `penv could not start \\`${executable}\\` to ${purpose}: ${detail}`,\n `Check that \\`${executable}\\` runs on its own — penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`,\n );\n }\n return new PenvError(\n \"RUN_COMMAND_NOT_STARTED\",\n `\\`${executable}\\` could not be started: ${detail}`,\n `Check the command after \\`--\\` runs on its own — \\`${executable}\\` has to be on PATH, exactly as it is spelled here.`,\n );\n}\n\ninterface SpawnTarget {\n readonly file: string;\n readonly args: readonly string[];\n /** True when the args are one pre-built command line rather than a list. */\n readonly verbatim: boolean;\n}\n\nfunction resolveTarget(\n executable: string,\n args: readonly string[],\n env: Readonly<Record<string, string | undefined>>,\n): SpawnTarget {\n if (process.platform !== \"win32\") {\n return { file: executable, args, verbatim: false };\n }\n const resolved = findExecutable(executable, env);\n if (resolved === undefined || !/\\.(cmd|bat)$/i.test(resolved)) {\n return { file: resolved ?? executable, args, verbatim: false };\n }\n return {\n file: env.ComSpec ?? \"cmd.exe\",\n args: [\"/d\", \"/s\", \"/c\", `\"${cmdCommandLine(resolved, args)}\"`],\n verbatim: true,\n };\n}\n\n/** A package-manager shim, which re-invokes cmd on its own way through. */\nconst SHIM = /(?:^|\\\\)node_modules\\\\\\.bin\\\\[^\\\\]+\\.cmd$/i;\n\n/**\n * The one command line cmd.exe is handed, escaped so the child receives the\n * bytes penv was given.\n *\n * The path is normalized first and *then* judged: `./node_modules/.bin/next.cmd`\n * and `.\\node_modules\\.bin\\next.cmd` are the same shim, and deciding on the\n * un-normalized spelling would escape a forward-slash invocation once while cmd\n * expands it twice — so an argument holding `&` would run as a command inside\n * the shim's second round. Windows' own separator, whatever this process runs\n * on, because this line is only ever read by cmd.exe.\n */\nexport function cmdCommandLine(resolved: string, args: readonly string[]): string {\n const command = win32.normalize(resolved);\n const shim = SHIM.test(command);\n return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(\n \" \",\n );\n}\n\n/**\n * The extensions a name is tried with, in the order the platform's own launcher\n * tries them.\n *\n * On Windows PATHEXT leads and the bare name comes last, because the bare name\n * is almost never what Windows would run: `pnpm`, `npx` and every\n * `node_modules/.bin` tool ship an extensionless POSIX shell script *beside*\n * their `.CMD` shim, in the same directory. Trying the empty extension first\n * matched that script, which is not executable by CreateProcess and is not a\n * `.cmd`, so the wrapper below was skipped and the spawn failed with ENOENT.\n * Everywhere else there are no extensions at all.\n */\nfunction extensions(\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform,\n): string[] {\n if (platform !== \"win32\") {\n return [\"\"];\n }\n const declared = env.PATHEXT ?? \".COM;.EXE;.BAT;.CMD\";\n return [...declared.split(\";\").filter((extension) => extension.length > 0), \"\"];\n}\n\n/**\n * What the shell would have run, found the way the shell finds it: the name as\n * given if it carries a path, else each PATH directory, each with each\n * executable extension.\n *\n * `platform` is a parameter so the ordering above is testable on either kind of\n * machine — it is the whole behavior, and it differs by platform.\n */\nexport function findExecutable(\n executable: string,\n env: Readonly<Record<string, string | undefined>>,\n platform: NodeJS.Platform = process.platform,\n): string | undefined {\n const candidates = extensions(env, platform);\n const isFile = (path: string): boolean => existsSync(path) && statSync(path).isFile();\n\n if (executable.includes(\"/\") || executable.includes(\"\\\\\") || isAbsolute(executable)) {\n return candidates.map((extension) => executable + extension).find(isFile);\n }\n const path = env.PATH ?? env.Path ?? \"\";\n for (const directory of path.split(delimiter).filter((entry) => entry.length > 0)) {\n const hit = candidates.map((extension) => join(directory, executable + extension)).find(isFile);\n if (hit !== undefined) {\n return hit;\n }\n }\n return undefined;\n}\n\n/** The characters cmd.exe expands before the program ever sees them. */\nconst CMD_METACHARACTERS = /([()\\][%!^\"`<>&|;, *?])/g;\n\n/** The command's own path: cmd's metacharacters escaped, and no quotes to confuse it. */\nfunction escapeCommand(command: string): string {\n return command.replace(CMD_METACHARACTERS, \"^$1\");\n}\n\n/**\n * One argument, quoted so the child's runtime splits it exactly where penv was\n * given it, then escaped so cmd.exe passes those quotes through instead of\n * acting on them.\n */\nfunction escapeArgument(argument: string, doubleEscape: boolean): string {\n const quoted = `\"${argument.replace(/(\\\\*)\"/g, '$1$1\\\\\"').replace(/(\\\\*)$/, \"$1$1\")}\"`;\n const escaped = quoted.replace(CMD_METACHARACTERS, \"^$1\");\n return doubleEscape ? escaped.replace(CMD_METACHARACTERS, \"^$1\") : escaped;\n}\n"],"mappings":"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","names":["existsSync","statSync","join","PenvError","path","PenvError","existsSync","join","statSync"]}
package/dist/index.cjs CHANGED
@@ -208,6 +208,7 @@ function escapeArgument(argument, doubleEscape) {
208
208
  var import_meta = {};
209
209
  var RUNTIME_PACKAGE = "@penvhq/penv";
210
210
  var SCHEMA_PACKAGE = "zod";
211
+ var TYPES_PACKAGE = "@penvhq/core";
211
212
  var LOCKFILES = [
212
213
  ["pnpm", "pnpm-lock.yaml"],
213
214
  ["yarn", "yarn.lock"],
@@ -310,6 +311,10 @@ function declaredIn(dir, name) {
310
311
  }
311
312
  return void 0;
312
313
  }
314
+ function blockPresentIn(dir, dev) {
315
+ const block = manifestOf(dir)?.[dev ? "devDependencies" : "dependencies"];
316
+ return block !== null && typeof block === "object" && !Array.isArray(block);
317
+ }
313
318
  function isPnpmWorkspaceRoot(root) {
314
319
  return (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, PNPM_WORKSPACE)) && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, "package.json"));
315
320
  }
@@ -433,6 +438,8 @@ function stepFor(manager, manifest, packages, options) {
433
438
  manifest,
434
439
  packages,
435
440
  command: addCommand(manager, options, specs),
441
+ dev: options.dev,
442
+ blockPresent: blockPresentIn(options.dir, options.dev),
436
443
  satisfied: pending.length === 0
437
444
  };
438
445
  }
@@ -444,6 +451,7 @@ function planInstall(root, version = engineVersion()) {
444
451
  const workspaceRoot = manager === "pnpm" && isPnpmWorkspaceRoot(root);
445
452
  const runtime = declaredIn(root, RUNTIME_PACKAGE);
446
453
  const zod = declaredIn(root, SCHEMA_PACKAGE);
454
+ const types = declaredIn(root, TYPES_PACKAGE);
447
455
  const packages = [
448
456
  {
449
457
  name: RUNTIME_PACKAGE,
@@ -460,34 +468,47 @@ function planInstall(root, version = engineVersion()) {
460
468
  satisfied: zod !== void 0
461
469
  }
462
470
  ];
471
+ const typesPackage = {
472
+ name: TYPES_PACKAGE,
473
+ version,
474
+ ...types === void 0 ? {} : { declared: types.version },
475
+ // Any declared version counts, for zod's reason: the augmentation binds on
476
+ // the module resolving, not on which release of it a project pinned.
477
+ satisfied: types !== void 0
478
+ };
463
479
  const pending = packages.filter((entry) => !entry.satisfied);
464
480
  const steps = [
465
481
  stepFor(manager, "package.json", packages, {
466
482
  workspaceRoot,
483
+ dir: root,
467
484
  dev: runtime?.dev === true && pending.every((entry) => entry.name === RUNTIME_PACKAGE) && pending.length > 0
468
- })
485
+ }),
486
+ stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dir: root, dev: true })
469
487
  ];
470
- for (const dir of workspaceMembers(root, RUNTIME_PACKAGE)) {
471
- const declared = declaredIn(dir, RUNTIME_PACKAGE);
472
- steps.push(
473
- stepFor(
474
- manager,
475
- manifestPathOf(root, dir),
476
- [
488
+ for (const name of [RUNTIME_PACKAGE, TYPES_PACKAGE]) {
489
+ for (const dir of workspaceMembers(root, name)) {
490
+ const declared = declaredIn(dir, name);
491
+ steps.push(
492
+ stepFor(
493
+ manager,
494
+ manifestPathOf(root, dir),
495
+ [
496
+ {
497
+ name,
498
+ version,
499
+ declared: declared.version,
500
+ satisfied: declared.version === version
501
+ }
502
+ ],
477
503
  {
478
- name: RUNTIME_PACKAGE,
479
- version,
480
- declared: declared.version,
481
- satisfied: declared.version === version
504
+ filter: `./${(0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).join("/")}`,
505
+ workspaceRoot: false,
506
+ dir,
507
+ dev: declared.dev
482
508
  }
483
- ],
484
- {
485
- filter: `./${(0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).join("/")}`,
486
- workspaceRoot: false,
487
- dev: declared.dev
488
- }
489
- )
490
- );
509
+ )
510
+ );
511
+ }
491
512
  }
492
513
  return {
493
514
  root,
@@ -504,16 +525,25 @@ function installedPackages(plan2) {
504
525
  const pending = plan2.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);
505
526
  return [...new Map(pending.map((entry) => [entry.name, entry])).values()];
506
527
  }
528
+ function plannedPackages(plan2) {
529
+ const all = plan2.steps.flatMap((step) => step.packages);
530
+ return [...new Map(all.map((entry) => [entry.name, entry])).values()];
531
+ }
532
+ function series(values) {
533
+ return values.length < 2 ? values[0] ?? "" : `${values.slice(0, -1).join(", ")} and ${values.at(-1)}`;
534
+ }
507
535
  function renderStep(step) {
508
536
  const pending = step.packages.filter((entry) => !entry.satisfied);
509
537
  const added = pending.filter((entry) => entry.declared === void 0);
510
538
  const replaced = pending.filter((entry) => entry.declared !== void 0);
539
+ const block = step.dev ? "devDependencies" : "dependencies";
540
+ const edge = step.blockPresent ? " " : " +";
511
541
  return [
512
542
  step.manifest,
513
543
  ...added.length === 0 ? [] : [
514
- ' + "dependencies": {',
544
+ `${edge} "${block}": {`,
515
545
  ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
516
- " + }"
546
+ `${edge} }`
517
547
  ],
518
548
  ...replaced.flatMap((entry) => [
519
549
  ` - "${entry.name}": "${entry.declared}"`,
@@ -523,9 +553,8 @@ function renderStep(step) {
523
553
  }
524
554
  function renderInstallPlan(plan2) {
525
555
  if (plan2.satisfied) {
526
- const packages = plan2.steps[0]?.packages ?? [];
527
556
  return [
528
- `package.json already has ${packages.map(describe).join(" and ")} \u2014 nothing to install.`
557
+ `package.json already has ${series(plannedPackages(plan2).map(describe))} \u2014 nothing to install.`
529
558
  ];
530
559
  }
531
560
  const pending = plan2.steps.filter((step) => !step.satisfied);