@penvhq/cli 0.16.2 → 1.0.0-alpha.3

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/install.cjs DELETED
@@ -1,587 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/install.ts
21
- var install_exports = {};
22
- __export(install_exports, {
23
- RUNTIME_PACKAGE: () => RUNTIME_PACKAGE,
24
- SCHEMA_PACKAGE: () => SCHEMA_PACKAGE,
25
- TYPES_PACKAGE: () => TYPES_PACKAGE,
26
- detectPackageManager: () => detectPackageManager,
27
- engineVersion: () => engineVersion,
28
- installFailed: () => installFailed,
29
- installWithPackageManager: () => installWithPackageManager,
30
- installedByManifest: () => installedByManifest,
31
- installedPackages: () => installedPackages,
32
- isPnpmWorkspaceRoot: () => isPnpmWorkspaceRoot,
33
- planInstall: () => planInstall,
34
- renderInstallPlan: () => renderInstallPlan,
35
- schemaPackageVersion: () => schemaPackageVersion
36
- });
37
- module.exports = __toCommonJS(install_exports);
38
- var import_node_fs2 = require("fs");
39
- var import_node_path2 = require("path");
40
- var import_core2 = require("@penvhq/core");
41
-
42
- // src/child.ts
43
- var import_node_child_process = require("child_process");
44
- var import_node_fs = require("fs");
45
- var import_node_path = require("path");
46
- var import_core = require("@penvhq/core");
47
- var FORWARDED = ["SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK"];
48
- var startChild = (invocation) => {
49
- const [executable, ...args] = invocation.command;
50
- if (executable === void 0) {
51
- throw noCommand();
52
- }
53
- const target = resolveTarget(executable, args, invocation.env);
54
- const child = (0, import_node_child_process.spawn)(target.file, target.args, {
55
- cwd: invocation.cwd,
56
- env: invocation.env,
57
- stdio: "inherit",
58
- ...target.verbatim ? { windowsVerbatimArguments: true } : {}
59
- });
60
- const forward = /* @__PURE__ */ new Map();
61
- for (const signal of FORWARDED) {
62
- const handler = () => {
63
- child.kill(signal);
64
- };
65
- forward.set(signal, handler);
66
- process.on(signal, handler);
67
- }
68
- const release = () => {
69
- for (const [signal, handler] of forward) {
70
- process.off(signal, handler);
71
- }
72
- };
73
- const ended = new Promise((resolve, reject) => {
74
- child.on("error", (cause) => {
75
- release();
76
- reject(cannotStart(executable, cause, invocation.purpose));
77
- });
78
- child.on("exit", (code, signal) => {
79
- release();
80
- resolve({ exitCode: code ?? 1, signal });
81
- });
82
- });
83
- return {
84
- ended,
85
- kill(signal) {
86
- child.kill(signal);
87
- }
88
- };
89
- };
90
- function noCommand() {
91
- return new import_core.PenvError(
92
- "RUN_NO_COMMAND",
93
- "`penv run` was given no command to start",
94
- "Put the command after `--`, e.g. `penv run -- pnpm dev`."
95
- );
96
- }
97
- function cannotStart(executable, cause, purpose) {
98
- const detail = cause instanceof Error ? cause.message : String(cause);
99
- if (purpose !== void 0) {
100
- return new import_core.PenvError(
101
- "PENV_COMMAND_NOT_STARTED",
102
- `penv could not start \`${executable}\` to ${purpose}: ${detail}`,
103
- `Check that \`${executable}\` runs on its own \u2014 penv starts it the way your shell does, so it has to be on PATH. Nothing was changed.`
104
- );
105
- }
106
- return new import_core.PenvError(
107
- "RUN_COMMAND_NOT_STARTED",
108
- `\`${executable}\` could not be started: ${detail}`,
109
- `Check the command after \`--\` runs on its own \u2014 \`${executable}\` has to be on PATH, exactly as it is spelled here.`
110
- );
111
- }
112
- function resolveTarget(executable, args, env) {
113
- if (process.platform !== "win32") {
114
- return { file: executable, args, verbatim: false };
115
- }
116
- const resolved = findExecutable(executable, env);
117
- if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved)) {
118
- return { file: resolved ?? executable, args, verbatim: false };
119
- }
120
- return {
121
- file: env.ComSpec ?? "cmd.exe",
122
- args: ["/d", "/s", "/c", `"${cmdCommandLine(resolved, args)}"`],
123
- verbatim: true
124
- };
125
- }
126
- var SHIM = /(?:^|\\)node_modules\\\.bin\\[^\\]+\.cmd$/i;
127
- function cmdCommandLine(resolved, args) {
128
- const command = import_node_path.win32.normalize(resolved);
129
- const shim = SHIM.test(command);
130
- return [escapeCommand(command), ...args.map((argument) => escapeArgument(argument, shim))].join(
131
- " "
132
- );
133
- }
134
- function extensions(env, platform) {
135
- if (platform !== "win32") {
136
- return [""];
137
- }
138
- const declared = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
139
- return [...declared.split(";").filter((extension) => extension.length > 0), ""];
140
- }
141
- function findExecutable(executable, env, platform = process.platform) {
142
- const candidates = extensions(env, platform);
143
- const isFile = (path2) => (0, import_node_fs.existsSync)(path2) && (0, import_node_fs.statSync)(path2).isFile();
144
- if (executable.includes("/") || executable.includes("\\") || (0, import_node_path.isAbsolute)(executable)) {
145
- return candidates.map((extension) => executable + extension).find(isFile);
146
- }
147
- const path = env.PATH ?? env.Path ?? "";
148
- for (const directory of path.split(import_node_path.delimiter).filter((entry) => entry.length > 0)) {
149
- const hit = candidates.map((extension) => (0, import_node_path.join)(directory, executable + extension)).find(isFile);
150
- if (hit !== void 0) {
151
- return hit;
152
- }
153
- }
154
- return void 0;
155
- }
156
- var CMD_METACHARACTERS = /([()\][%!^"`<>&|;, *?])/g;
157
- function escapeCommand(command) {
158
- return command.replace(CMD_METACHARACTERS, "^$1");
159
- }
160
- function escapeArgument(argument, doubleEscape) {
161
- const quoted = `"${argument.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\*)$/, "$1$1")}"`;
162
- const escaped = quoted.replace(CMD_METACHARACTERS, "^$1");
163
- return doubleEscape ? escaped.replace(CMD_METACHARACTERS, "^$1") : escaped;
164
- }
165
-
166
- // src/install.ts
167
- var import_meta = {};
168
- var RUNTIME_PACKAGE = "@penvhq/penv";
169
- var SCHEMA_PACKAGE = "zod";
170
- var TYPES_PACKAGE = "@penvhq/core";
171
- var LOCKFILES = [
172
- ["pnpm", "pnpm-lock.yaml"],
173
- ["yarn", "yarn.lock"],
174
- ["bun", "bun.lock"],
175
- ["bun", "bun.lockb"],
176
- ["npm", "package-lock.json"]
177
- ];
178
- var ADD = {
179
- pnpm: ["pnpm", "add"],
180
- npm: ["npm", "install"],
181
- yarn: ["yarn", "add"],
182
- bun: ["bun", "add"]
183
- };
184
- var EXACT = {
185
- pnpm: "--save-exact",
186
- npm: "--save-exact",
187
- yarn: "--exact",
188
- bun: "--exact"
189
- };
190
- var DEV = {
191
- pnpm: "-D",
192
- npm: "--save-dev",
193
- yarn: "--dev",
194
- bun: "--dev"
195
- };
196
- var WORKSPACE_ROOT_FLAG = "-w";
197
- var PNPM_WORKSPACE = "pnpm-workspace.yaml";
198
- function engineVersion() {
199
- const version = ownManifest()?.version;
200
- if (typeof version === "string" && version.length > 0) {
201
- return version;
202
- }
203
- throw new import_core2.PenvError(
204
- "ENGINE_VERSION_UNREADABLE",
205
- "penv could not read its own version, so it cannot say which `@penvhq/penv` this project needs",
206
- `Reinstall penv, then run \`penv init\` again.`
207
- );
208
- }
209
- function schemaPackageVersion() {
210
- const peers = ownManifest()?.peerDependencies;
211
- const declared = peers !== null && typeof peers === "object" && !Array.isArray(peers) ? peers[SCHEMA_PACKAGE] : void 0;
212
- const floor = typeof declared === "string" ? declared.replace(/^[\^~>=\s]+/, "").trim() : "";
213
- if (floor.length > 0) {
214
- return floor;
215
- }
216
- throw new import_core2.PenvError(
217
- "ENGINE_PEER_UNREADABLE",
218
- `penv could not read its own \`${SCHEMA_PACKAGE}\` peer range, so it cannot say which ${SCHEMA_PACKAGE} this project needs`,
219
- `Reinstall penv, then run \`penv init\` again.`
220
- );
221
- }
222
- function ownManifest() {
223
- try {
224
- const parsed = JSON.parse(
225
- (0, import_node_fs2.readFileSync)(new URL("../package.json", import_meta.url), "utf8")
226
- );
227
- return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
228
- } catch {
229
- return void 0;
230
- }
231
- }
232
- function detectPackageManager(root) {
233
- for (const [manager, lockfile] of LOCKFILES) {
234
- if ((0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, lockfile))) {
235
- return manager;
236
- }
237
- }
238
- return declaredManager(root) ?? "npm";
239
- }
240
- function declaredManager(root) {
241
- const declared = manifestOf(root)?.packageManager;
242
- if (typeof declared !== "string") {
243
- return void 0;
244
- }
245
- const name = declared.split("@")[0];
246
- return name === "pnpm" || name === "npm" || name === "yarn" || name === "bun" ? name : void 0;
247
- }
248
- function manifestOf(root) {
249
- const file = (0, import_node_path2.join)(root, "package.json");
250
- if (!(0, import_node_fs2.existsSync)(file)) {
251
- return void 0;
252
- }
253
- try {
254
- const manifest = JSON.parse((0, import_node_fs2.readFileSync)(file, "utf8"));
255
- return manifest !== null && typeof manifest === "object" && !Array.isArray(manifest) ? manifest : void 0;
256
- } catch {
257
- return void 0;
258
- }
259
- }
260
- function declaredIn(dir, name) {
261
- const manifest = manifestOf(dir);
262
- for (const field of ["dependencies", "devDependencies"]) {
263
- const block = manifest?.[field];
264
- if (block !== null && typeof block === "object" && !Array.isArray(block)) {
265
- const version = block[name];
266
- if (typeof version === "string") {
267
- return { version, dev: field === "devDependencies" };
268
- }
269
- }
270
- }
271
- return void 0;
272
- }
273
- function blockPresentIn(dir, dev) {
274
- const block = manifestOf(dir)?.[dev ? "devDependencies" : "dependencies"];
275
- return block !== null && typeof block === "object" && !Array.isArray(block);
276
- }
277
- function isPnpmWorkspaceRoot(root) {
278
- 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"));
279
- }
280
- function workspaceGlobs(root) {
281
- let text;
282
- try {
283
- text = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(root, PNPM_WORKSPACE), "utf8");
284
- } catch {
285
- return [];
286
- }
287
- const unquote = (raw) => raw.replace(/^['"]|['"]$/g, "").trim();
288
- const globs = [];
289
- let inside = false;
290
- for (const line of text.split(/\r?\n/)) {
291
- const flow = /^packages:\s*\[(.*)\]\s*$/.exec(line);
292
- if (flow?.[1] !== void 0) {
293
- return flow[1].split(",").map(unquote).filter((glob) => glob !== "");
294
- }
295
- if (/^packages:\s*$/.test(line)) {
296
- inside = true;
297
- continue;
298
- }
299
- if (!inside) {
300
- continue;
301
- }
302
- const item = /^\s+-\s*(.+?)\s*$/.exec(line);
303
- if (item?.[1] !== void 0) {
304
- globs.push(unquote(item[1]));
305
- continue;
306
- }
307
- if (line.trim() !== "" && !line.trimStart().startsWith("#")) {
308
- break;
309
- }
310
- }
311
- return globs;
312
- }
313
- function directoriesIn(dir) {
314
- try {
315
- return (0, import_node_fs2.readdirSync)(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "node_modules").map((entry) => (0, import_node_path2.join)(dir, entry.name));
316
- } catch {
317
- return [];
318
- }
319
- }
320
- function isDirectory(path) {
321
- try {
322
- return (0, import_node_fs2.statSync)(path).isDirectory();
323
- } catch {
324
- return false;
325
- }
326
- }
327
- function expandGlob(root, glob) {
328
- let dirs = [root];
329
- for (const segment of glob.split("/").filter((part) => part !== "" && part !== ".")) {
330
- const next = [];
331
- for (const dir of dirs) {
332
- if (segment === "**") {
333
- const stack = [dir];
334
- while (stack.length > 0) {
335
- const current = stack.pop();
336
- next.push(current);
337
- stack.push(...directoriesIn(current));
338
- }
339
- continue;
340
- }
341
- if (segment.includes("*")) {
342
- const pattern = new RegExp(
343
- `^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")}$`
344
- );
345
- next.push(
346
- ...directoriesIn(dir).filter((child) => pattern.test(child.slice(dir.length + 1)))
347
- );
348
- continue;
349
- }
350
- const candidate = (0, import_node_path2.join)(dir, segment);
351
- if (isDirectory(candidate)) {
352
- next.push(candidate);
353
- }
354
- }
355
- dirs = next;
356
- }
357
- return dirs;
358
- }
359
- function workspaceMembers(root, name) {
360
- if (!isPnpmWorkspaceRoot(root)) {
361
- return [];
362
- }
363
- const globs = workspaceGlobs(root);
364
- const excluded = globs.filter((glob) => glob.startsWith("!")).flatMap((glob) => expandGlob(root, glob.slice(1)));
365
- const found = /* @__PURE__ */ new Set();
366
- for (const glob of globs.filter((entry) => !entry.startsWith("!"))) {
367
- for (const dir of expandGlob(root, glob)) {
368
- if (dir !== root && !excluded.includes(dir) && declaredIn(dir, name) !== void 0) {
369
- found.add(dir);
370
- }
371
- }
372
- }
373
- return [...found].sort();
374
- }
375
- function manifestPathOf(root, dir) {
376
- const within = (0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).filter((part) => part !== "");
377
- return [...within, "package.json"].join("/");
378
- }
379
- function addCommand(manager, options, specs) {
380
- const [bin, verb] = ADD[manager];
381
- return [
382
- bin,
383
- ...options.filter === void 0 ? [] : ["--filter", options.filter],
384
- verb,
385
- ...options.workspaceRoot ? [WORKSPACE_ROOT_FLAG] : [],
386
- EXACT[manager],
387
- ...options.dev ? [DEV[manager]] : [],
388
- ...specs
389
- ];
390
- }
391
- function stepFor(manager, manifest, packages, options) {
392
- const pending = packages.filter((entry) => !entry.satisfied);
393
- const specs = (pending.length === 0 ? packages : pending).map(
394
- (entry) => `${entry.name}@${entry.version}`
395
- );
396
- return {
397
- manifest,
398
- packages,
399
- command: addCommand(manager, options, specs),
400
- dev: options.dev,
401
- blockPresent: blockPresentIn(options.dir, options.dev),
402
- satisfied: pending.length === 0
403
- };
404
- }
405
- function planInstall(root, version = engineVersion()) {
406
- const manager = detectPackageManager(root);
407
- const lockfile = LOCKFILES.find(
408
- ([name, file]) => name === manager && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(root, file))
409
- )?.[1];
410
- const workspaceRoot = manager === "pnpm" && isPnpmWorkspaceRoot(root);
411
- const runtime = declaredIn(root, RUNTIME_PACKAGE);
412
- const zod = declaredIn(root, SCHEMA_PACKAGE);
413
- const types = declaredIn(root, TYPES_PACKAGE);
414
- const packages = [
415
- {
416
- name: RUNTIME_PACKAGE,
417
- version,
418
- ...runtime === void 0 ? {} : { declared: runtime.version },
419
- satisfied: runtime?.version === version
420
- },
421
- {
422
- name: SCHEMA_PACKAGE,
423
- version: schemaPackageVersion(),
424
- ...zod === void 0 ? {} : { declared: zod.version },
425
- // Any declared zod counts: which zod a project uses is the project's
426
- // decision, and penv is here to make sure there is one, not to move it.
427
- satisfied: zod !== void 0
428
- }
429
- ];
430
- const typesPackage = {
431
- name: TYPES_PACKAGE,
432
- version,
433
- ...types === void 0 ? {} : { declared: types.version },
434
- // Held to the pin, exactly as a workspace member's copy is. The augmentation
435
- // binds on the module resolving, but what it binds to is whatever release
436
- // resolved: a core behind the pin checks `penv.config.ts` against a shape the
437
- // engine no longer has, and the committed declarations augment interfaces
438
- // that moved under them.
439
- satisfied: types?.version === version
440
- };
441
- const pending = packages.filter((entry) => !entry.satisfied);
442
- const steps = [
443
- stepFor(manager, "package.json", packages, {
444
- workspaceRoot,
445
- dir: root,
446
- dev: runtime?.dev === true && pending.every((entry) => entry.name === RUNTIME_PACKAGE) && pending.length > 0
447
- }),
448
- stepFor(manager, "package.json", [typesPackage], { workspaceRoot, dir: root, dev: true })
449
- ];
450
- for (const name of [RUNTIME_PACKAGE, TYPES_PACKAGE]) {
451
- for (const dir of workspaceMembers(root, name)) {
452
- const declared = declaredIn(dir, name);
453
- steps.push(
454
- stepFor(
455
- manager,
456
- manifestPathOf(root, dir),
457
- [
458
- {
459
- name,
460
- version,
461
- declared: declared.version,
462
- satisfied: declared.version === version
463
- }
464
- ],
465
- {
466
- filter: `./${(0, import_node_path2.relative)(root, dir).split(import_node_path2.sep).join("/")}`,
467
- workspaceRoot: false,
468
- dir,
469
- dev: declared.dev
470
- }
471
- )
472
- );
473
- }
474
- }
475
- return {
476
- root,
477
- manager,
478
- steps,
479
- ...lockfile === void 0 ? {} : { lockfile },
480
- satisfied: steps.every((step) => step.satisfied)
481
- };
482
- }
483
- function describe(entry) {
484
- return `${entry.name} ${entry.version}`;
485
- }
486
- function installedPackages(plan) {
487
- const pending = plan.steps.flatMap((step) => step.packages).filter((entry) => !entry.satisfied);
488
- return [...new Map(pending.map((entry) => [entry.name, entry])).values()];
489
- }
490
- function installedByManifest(plan) {
491
- const byManifest = /* @__PURE__ */ new Map();
492
- for (const step of plan.steps.filter((entry) => !entry.satisfied)) {
493
- const landed = byManifest.get(step.manifest) ?? [];
494
- landed.push(...step.packages.filter((entry) => !entry.satisfied));
495
- byManifest.set(step.manifest, landed);
496
- }
497
- return [...byManifest].map(([manifest, packages]) => ({
498
- manifest,
499
- packages: series(
500
- [...new Map(packages.map((entry) => [entry.name, entry])).values()].map(describe)
501
- )
502
- }));
503
- }
504
- function plannedPackages(plan) {
505
- const all = plan.steps.flatMap((step) => step.packages);
506
- return [...new Map(all.map((entry) => [entry.name, entry])).values()];
507
- }
508
- function series(values) {
509
- return values.length < 2 ? values[0] ?? "" : `${values.slice(0, -1).join(", ")} and ${values.at(-1)}`;
510
- }
511
- function renderStep(step) {
512
- const pending = step.packages.filter((entry) => !entry.satisfied);
513
- const added = pending.filter((entry) => entry.declared === void 0);
514
- const replaced = pending.filter((entry) => entry.declared !== void 0);
515
- const block = step.dev ? "devDependencies" : "dependencies";
516
- const edge = step.blockPresent ? " " : " +";
517
- return [
518
- step.manifest,
519
- ...added.length === 0 ? [] : [
520
- `${edge} "${block}": {`,
521
- ...added.map((entry) => ` + "${entry.name}": "${entry.version}"`),
522
- `${edge} }`
523
- ],
524
- ...replaced.flatMap((entry) => [
525
- ` - "${entry.name}": "${entry.declared}"`,
526
- ` + "${entry.name}": "${entry.version}"`
527
- ])
528
- ];
529
- }
530
- function renderInstallPlan(plan) {
531
- if (plan.satisfied) {
532
- return [
533
- `package.json already has ${series(plannedPackages(plan).map(describe))} \u2014 nothing to install.`
534
- ];
535
- }
536
- const pending = plan.steps.filter((step) => !step.satisfied);
537
- const [first, ...rest] = pending.map((step) => step.command.join(" "));
538
- const landing = installedPackages(plan).map((entry) => ` + ${entry.name}@${entry.version}`);
539
- return [
540
- ...pending.flatMap(renderStep),
541
- ...plan.lockfile === void 0 ? [] : [plan.lockfile, ...landing],
542
- "",
543
- `Run with: ${first ?? ""}`,
544
- ...rest.map((command) => ` then ${command}`)
545
- ];
546
- }
547
- var installWithPackageManager = async (plan) => {
548
- for (const step of plan.steps) {
549
- if (step.satisfied) {
550
- continue;
551
- }
552
- const child = startChild({
553
- command: step.command,
554
- env: process.env,
555
- cwd: plan.root,
556
- purpose: `install ${step.packages.map(describe).join(" and ")} in ${step.manifest}`
557
- });
558
- const ended = await child.ended;
559
- if (ended.exitCode !== 0 || ended.signal !== null) {
560
- throw installFailed(plan, step);
561
- }
562
- }
563
- };
564
- function installFailed(plan, step) {
565
- return new import_core2.PenvError(
566
- "INIT_INSTALL_FAILED",
567
- `${step.command.join(" ")} did not finish, so penv migrated nothing`,
568
- `Read what ${plan.manager} printed above \u2014 it names what it refused. Fix that and run this command again; your dotenv files are exactly where they were.`
569
- );
570
- }
571
- // Annotate the CommonJS export names for ESM import in node:
572
- 0 && (module.exports = {
573
- RUNTIME_PACKAGE,
574
- SCHEMA_PACKAGE,
575
- TYPES_PACKAGE,
576
- detectPackageManager,
577
- engineVersion,
578
- installFailed,
579
- installWithPackageManager,
580
- installedByManifest,
581
- installedPackages,
582
- isPnpmWorkspaceRoot,
583
- planInstall,
584
- renderInstallPlan,
585
- schemaPackageVersion
586
- });
587
- //# sourceMappingURL=install.cjs.map