@monimesl/dot-cli 0.1.0-alpha.7 → 0.1.0-alpha.9

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.
Files changed (57) hide show
  1. package/README.md +17 -9
  2. package/dist/cli.js +158 -71
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-DFOUQnLA.js → server-BfPAnsSf.js} +34 -81
  5. package/dist/{server-DFOUQnLA.js.map → server-BfPAnsSf.js.map} +1 -1
  6. package/dist/templates/showcase/.env +1 -1
  7. package/dist/templates/showcase/dot.ts +2 -0
  8. package/dist/templates/showcase/pages/cache/controller.ts +45 -0
  9. package/dist/templates/showcase/pages/cache/state.ts +17 -0
  10. package/dist/templates/showcase/pages/cache/view.dml +36 -0
  11. package/dist/templates/showcase/pages/carousel/controller.ts +33 -3
  12. package/dist/templates/showcase/pages/carousel/state.ts +61 -24
  13. package/dist/templates/showcase/pages/carousel/view.dml +18 -15
  14. package/dist/templates/showcase/pages/device/controller.ts +22 -12
  15. package/dist/templates/showcase/pages/device/state.ts +1 -1
  16. package/dist/templates/showcase/pages/device/view.dml +13 -8
  17. package/dist/templates/showcase/pages/feedback/controller.ts +6 -1
  18. package/dist/templates/showcase/pages/feedback/state.ts +1 -0
  19. package/dist/templates/showcase/pages/feedback/view.dml +23 -23
  20. package/dist/templates/showcase/pages/files/controller.ts +33 -14
  21. package/dist/templates/showcase/pages/files/state.ts +10 -2
  22. package/dist/templates/showcase/pages/files/view.dml +29 -14
  23. package/dist/templates/showcase/pages/filters/controller.ts +11 -12
  24. package/dist/templates/showcase/pages/filters/state.ts +107 -61
  25. package/dist/templates/showcase/pages/filters/view.dml +18 -17
  26. package/dist/templates/showcase/pages/forms/controller.ts +36 -12
  27. package/dist/templates/showcase/pages/forms/state.ts +25 -6
  28. package/dist/templates/showcase/pages/forms/view.dml +28 -18
  29. package/dist/templates/showcase/pages/images/controller.ts +26 -0
  30. package/dist/templates/showcase/pages/images/state.ts +3 -0
  31. package/dist/templates/showcase/pages/images/view.dml +18 -3
  32. package/dist/templates/showcase/pages/index/state.ts +26 -14
  33. package/dist/templates/showcase/pages/index/view.dml +2 -2
  34. package/dist/templates/showcase/pages/layout/controller.ts +36 -2
  35. package/dist/templates/showcase/pages/layout/state.ts +22 -2
  36. package/dist/templates/showcase/pages/layout/view.dml +45 -22
  37. package/dist/templates/showcase/pages/lists/controller.ts +12 -11
  38. package/dist/templates/showcase/pages/lists/state.ts +87 -66
  39. package/dist/templates/showcase/pages/lists/view.dml +15 -15
  40. package/dist/templates/showcase/pages/navigation/controller.ts +2 -1
  41. package/dist/templates/showcase/pages/navigation/view.dml +5 -5
  42. package/dist/templates/showcase/pages/network/controller.ts +68 -24
  43. package/dist/templates/showcase/pages/network/state.ts +13 -6
  44. package/dist/templates/showcase/pages/network/view.dml +29 -18
  45. package/dist/templates/showcase/pages/payments/controller.ts +16 -12
  46. package/dist/templates/showcase/pages/payments/state.ts +3 -0
  47. package/dist/templates/showcase/pages/payments/view.dml +22 -12
  48. package/dist/templates/showcase/pages/secrets/controller.ts +43 -0
  49. package/dist/templates/showcase/pages/secrets/state.ts +5 -0
  50. package/dist/templates/showcase/pages/secrets/view.dml +24 -0
  51. package/dist/templates/showcase/pages/state/state.ts +1 -1
  52. package/dist/templates/showcase/pages/state/view.dml +9 -9
  53. package/dist/templates/showcase/pages/text/controller.ts +3 -3
  54. package/dist/templates/showcase/pages/text/state.ts +6 -5
  55. package/dist/templates/showcase/pages/text/view.dml +22 -13
  56. package/dist/templates/showcase/statics/logo.png +0 -0
  57. package/package.json +6 -6
package/README.md CHANGED
@@ -8,19 +8,20 @@ dotctl create [dir] # scaffold a dot (asks for its name and template)
8
8
  dotctl create page <path> # add a page to the dot you are in
9
9
  dotctl build [root] # compile the dot into dist/ui and dist/logic, minified
10
10
  dotctl dev [root] # build, run it in the browser simulator, and reload it on each change
11
+ dotctl preview [root] # build it as `build` does, and run that build in the browser simulator
11
12
  dotctl keys show # print this computer's developer key: its public key and key ID
12
13
  dotctl lsp # the .dml language server, which editors start
13
14
  ```
14
15
 
15
- | Option | Applies to | What it does |
16
- | --------------------- | ---------- | ------------------------------------------------------------------------ |
17
- | `--out-dir <dir>` | both | Output directory (default: `<root>/dist`) |
18
- | `--conditions <list>` | both | Extra export conditions, such as `@monimesl/dot-source` |
19
- | `--port <port>` | `dev` | Simulator port (default: `3680`, or the next free one) |
20
- | `--device` | `dev` | Also serve the dot to the Dot example apps on phones, through a QR code |
21
- | `--name <name>` | `create` | The dot's name, instead of asking |
22
- | `--template <name>` | `create` | `starter` (one page to build on, the default) or `showcase` (every page) |
23
- | `--pm <name>` | `create` | The package manager to install with: npm, pnpm, yarn or bun |
16
+ | Option | Applies to | What it does |
17
+ | --------------------- | ------------------------- | ------------------------------------------------------------------------ |
18
+ | `--out-dir <dir>` | `build`, `dev`, `preview` | Output directory (default: `<root>/dist`) |
19
+ | `--conditions <list>` | `build`, `dev`, `preview` | Extra export conditions, such as `@monimesl/dot-source` |
20
+ | `--port <port>` | `dev`, `preview` | Simulator port (default: `3680`, or the next free one) |
21
+ | `--device` | `dev` | Also serve the dot to the Dot example apps on phones, through a QR code |
22
+ | `--name <name>` | `create` | The dot's name, instead of asking |
23
+ | `--template <name>` | `create` | `starter` (one page to build on, the default) or `showcase` (every page) |
24
+ | `--pm <name>` | `create` | The package manager to install with: npm, pnpm, yarn or bun |
24
25
 
25
26
  ## `dotctl create`
26
27
 
@@ -45,6 +46,13 @@ With `--device`, it also serves each build on the local network to the Dot examp
45
46
  QR code to scan with one, which then follows the dot in the same way: see
46
47
  [the example apps](../hosts/native/README.md#the-example-apps).
47
48
 
49
+ ## `dotctl preview`
50
+
51
+ Builds the dot as `dotctl build` does, and runs that build in the browser simulator: minified, and
52
+ with only the components its pages use, as the host app gets it. Use it to check a build before
53
+ publishing it. It doesn't rebuild on change: run it again after editing. The dot keeps the files it
54
+ saved under `dotctl dev`.
55
+
48
56
  ## Developer keys
49
57
 
50
58
  Dash opens only links that one of the signed-in developer's keys signed. `dotctl dev --device`
package/dist/cli.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { spawn, spawnSync } from "node:child_process";
3
3
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
- import { createInterface } from "node:readline/promises";
5
- import { parseArgs } from "node:util";
4
+ import { parseArgs, styleText } from "node:util";
6
5
  import { CompileError, PAGES_DIR, build, installedRuntime, loadProject } from "@monimesl/dot-compiler";
7
6
  import { DEFAULT_PORT, startSimulator } from "@monimesl/dot-simulator";
8
7
  import { existsSync, readFileSync, readdirSync, watch } from "node:fs";
@@ -14,6 +13,8 @@ import { renderUnicodeCompact } from "uqr";
14
13
  import { createHash, createHmac, createPrivateKey, createPublicKey, randomBytes, sign, timingSafeEqual } from "node:crypto";
15
14
  import { homedir, networkInterfaces } from "node:os";
16
15
  import { createServer } from "node:http";
16
+ import { emitKeypressEvents } from "node:readline";
17
+ import { createInterface } from "node:readline/promises";
17
18
  //#region src/report.ts
18
19
  /** A mistake in how a command was used, which its message alone explains. */
19
20
  var CommandError = class extends Error {
@@ -36,6 +37,10 @@ function printBuildTime(startedAt) {
36
37
  //#endregion
37
38
  //#region src/create/template.ts
38
39
  const TEMPLATES = ["starter", "showcase"];
40
+ const TEMPLATE_HINTS = {
41
+ starter: "one page to build on",
42
+ showcase: "a page for each feature, to read"
43
+ };
39
44
  function isTemplate(name) {
40
45
  return TEMPLATES.includes(name);
41
46
  }
@@ -75,6 +80,7 @@ function packageJson({ packageName, dotVersion, workspace }) {
75
80
  scripts: {
76
81
  dev: `dotctl dev${conditions}`,
77
82
  build: `dotctl build${conditions}`,
83
+ preview: `dotctl preview${conditions}`,
78
84
  typecheck: "tsc --noEmit"
79
85
  },
80
86
  dependencies: { "@monimesl/dot-runtime": dotVersion },
@@ -115,8 +121,8 @@ function dotJson({ name, packageName, template }) {
115
121
  version: "0.1.0"
116
122
  };
117
123
  if (template === "showcase") dot.network = { hosts: [
118
- "api.github.com",
119
- "avatars.githubusercontent.com",
124
+ "itunes.apple.com",
125
+ "*.mzstatic.com",
120
126
  "httpbin.org"
121
127
  ] };
122
128
  return json(dot);
@@ -218,11 +224,17 @@ function cliRoot() {
218
224
  return dirname(createRequire(import.meta.url).resolve("@monimesl/dot-cli/package.json"));
219
225
  }
220
226
  //#endregion
227
+ //#region src/dot-root.ts
228
+ function findDotRoot(dir) {
229
+ while (true) {
230
+ if (existsSync(join(dir, "dot.json"))) return dir;
231
+ const parent = dirname(dir);
232
+ if (parent === dir) return;
233
+ dir = parent;
234
+ }
235
+ }
236
+ //#endregion
221
237
  //#region src/create/page.ts
222
- /**
223
- * Adds a page to the dot around `cwd`: its folder under pages/, with its three files, listed last in
224
- * dot.ts.
225
- */
226
238
  async function createPage(path, cwd) {
227
239
  if (!isPageId(path)) throw new CommandError(`"${path}" is not a page path: use letters, digits, "-" and "_", with "/" between folders, such as "cart/checkout"`);
228
240
  const root = findDotRoot(resolve(cwd));
@@ -277,14 +289,6 @@ function listsPage(source, dotFile, path) {
277
289
  return false;
278
290
  }
279
291
  }
280
- function findDotRoot(dir) {
281
- while (true) {
282
- if (existsSync(join(dir, "dot.json"))) return dir;
283
- const parent = dirname(dir);
284
- if (parent === dir) return;
285
- dir = parent;
286
- }
287
- }
288
292
  //#endregion
289
293
  //#region src/create/package-manager.ts
290
294
  const PACKAGE_MANAGERS = [
@@ -520,6 +524,17 @@ async function readText(request) {
520
524
  return Buffer.concat(chunks).toString("utf8");
521
525
  }
522
526
  //#endregion
527
+ //#region src/simulator.ts
528
+ function startDotSimulator(project, outDir, port) {
529
+ return startSimulator({
530
+ dotDir: outDir,
531
+ reactHostDir: installedRuntime(project.root).react.dir,
532
+ name: project.dotJson.name,
533
+ port,
534
+ storageDir: join(project.root, ".dot", "files")
535
+ });
536
+ }
537
+ //#endregion
523
538
  //#region src/dev.ts
524
539
  const CHANGE_DEBOUNCE_MS = 50;
525
540
  async function dev({ port, device, ...options }) {
@@ -543,13 +558,7 @@ async function dev({ port, device, ...options }) {
543
558
  }
544
559
  };
545
560
  const firstError = await buildApp();
546
- const simulator = await startSimulator({
547
- dotDir: outDir,
548
- reactHostDir: installedRuntime(project.root).react.dir,
549
- name: project.dotJson.name,
550
- port,
551
- storageDir: join(project.root, ".dot", "files")
552
- });
561
+ const simulator = await startDotSimulator(project, outDir, port);
553
562
  if (firstError) simulator.error(firstError);
554
563
  console.log(`\n Simulator running at: ${simulator.url}\n`);
555
564
  let devices;
@@ -601,11 +610,9 @@ async function dev({ port, device, ...options }) {
601
610
  };
602
611
  }
603
612
  function printDeviceLink(link) {
604
- console.log(" Scan this in Dash or a Dot example app on a phone:\n");
613
+ console.log(" Scan this in Dash: turn on Enable Dot Debugging in Developer Settings, then tap Live Test on\n the Dots tab.\n");
605
614
  console.log(renderUnicodeCompact(link, { border: 1 }).replace(/^/gm, " "));
606
- console.log(`\n Or open the link on a device, an emulator or a simulator:\n\n ${link}\n`);
607
- console.log(` Android emulator: adb shell am start -a android.intent.action.VIEW -d '${link}'`);
608
- console.log(` iOS Simulator: xcrun simctl openurl booted '${link}'\n`);
615
+ console.log(`\n ${link}\n`);
609
616
  }
610
617
  var RebuildQueue = class {
611
618
  #rebuild;
@@ -639,6 +646,99 @@ function isIgnored(file) {
639
646
  });
640
647
  }
641
648
  //#endregion
649
+ //#region src/preview.ts
650
+ async function preview({ port, ...options }) {
651
+ const project = await loadProject(options.root);
652
+ const outDir = resolve(project.root, options.outDir ?? "dist");
653
+ const started = performance.now();
654
+ printWarnings(await build({
655
+ ...options,
656
+ outDir,
657
+ mode: "production"
658
+ }));
659
+ printBuildTime(started);
660
+ const simulator = await startDotSimulator(project, outDir, port);
661
+ console.log(`\n Simulator running at: ${simulator.url}\n`);
662
+ return simulator;
663
+ }
664
+ //#endregion
665
+ //#region src/prompts.ts
666
+ const TERMINAL = {
667
+ input: process.stdin,
668
+ output: process.stdout
669
+ };
670
+ const ERASE_BELOW = "\x1B[0J";
671
+ const HIDE_CURSOR = "\x1B[?25l";
672
+ const SHOW_CURSOR = "\x1B[?25h";
673
+ const cursorUp = (lines) => `\x1b[${lines}A`;
674
+ async function select(question, choices, fallback, { input, output } = TERMINAL) {
675
+ if (!input.isTTY) return fallback;
676
+ let current = Math.max(0, choices.findIndex((choice) => choice.value === fallback));
677
+ const width = choices.some((choice) => choice.hint) ? Math.max(...choices.map((choice) => choice.value.length)) : 0;
678
+ const row = (choice, index) => {
679
+ const name = choice.value.padEnd(width);
680
+ const hint = choice.hint ? styleText("dim", ` ${choice.hint}`) : "";
681
+ const defaultMark = choice.value === fallback ? styleText("green", " (default)") : "";
682
+ if (index === current) return `${styleText("cyan", "❯")} ${styleText(["cyan", "bold"], name)}${hint}${defaultMark}`;
683
+ return ` ${name}${hint}${defaultMark}`;
684
+ };
685
+ let drawnLines = 0;
686
+ const draw = (lines) => {
687
+ output.write(`${drawnLines ? cursorUp(drawnLines) : ""}${ERASE_BELOW}${lines.join("\n")}\n`);
688
+ drawnLines = lines.length;
689
+ };
690
+ const drawList = () => draw([`${styleText("bold", question)} ${styleText("dim", "↑/↓ to move, Enter to choose")}`, ...choices.map(row)]);
691
+ emitKeypressEvents(input);
692
+ input.setRawMode(true);
693
+ input.resume();
694
+ output.write(HIDE_CURSOR);
695
+ drawList();
696
+ return new Promise((resolve) => {
697
+ const onKeypress = (_text, key) => {
698
+ if (key.ctrl && key.name === "c") {
699
+ output.write(`${SHOW_CURSOR}\n`);
700
+ process.exit(130);
701
+ }
702
+ if (key.name === "up" || key.name === "k") {
703
+ current = (current - 1 + choices.length) % choices.length;
704
+ drawList();
705
+ } else if (key.name === "down" || key.name === "j") {
706
+ current = (current + 1) % choices.length;
707
+ drawList();
708
+ } else if (key.name === "return" || key.name === "enter") {
709
+ const { value } = choices[current];
710
+ input.off("keypress", onKeypress);
711
+ input.setRawMode(false);
712
+ input.pause();
713
+ draw([`${styleText("bold", question)} ${styleText("cyan", value)}`]);
714
+ output.write(SHOW_CURSOR);
715
+ resolve(value);
716
+ }
717
+ };
718
+ input.on("keypress", onKeypress);
719
+ });
720
+ }
721
+ async function confirm(question, fallback, terminal = TERMINAL) {
722
+ if (!terminal.input.isTTY) return false;
723
+ return await select(question, [{ value: "Yes" }, { value: "No" }], fallback ? "Yes" : "No", terminal) === "Yes";
724
+ }
725
+ async function ask(question, fallback, { input, output } = TERMINAL) {
726
+ if (!input.isTTY) return fallback;
727
+ const hint = fallback ? styleText("dim", ` (default: ${fallback})`) : "";
728
+ const readline = createInterface({
729
+ input,
730
+ output
731
+ });
732
+ try {
733
+ return (await readline.question(`${styleText("bold", question)}${hint}: `)).trim() || fallback;
734
+ } catch (error) {
735
+ if (error.name === "AbortError") return;
736
+ throw error;
737
+ } finally {
738
+ readline.close();
739
+ }
740
+ }
741
+ //#endregion
642
742
  //#region src/cli.ts
643
743
  const USAGE = `Usage:
644
744
  dotctl create [dir] [--name <name>] [--template <starter|showcase>]
@@ -650,6 +750,8 @@ const USAGE = `Usage:
650
750
  view.dml, state.ts and controller.ts
651
751
  dotctl build [root] [options] Build the minified bundles the host app runs
652
752
  dotctl dev [root] [options] Run in the browser simulator, rebuilding on change
753
+ dotctl preview [root] [options] Build as \`build\` does, and run that build in the browser
754
+ simulator, as the host app gets it
653
755
  dotctl keys show Show the public key and key ID of this computer's
654
756
  developer key, which signs the links \`dev --device\`
655
757
  shows, for Dash to open
@@ -659,8 +761,8 @@ const USAGE = `Usage:
659
761
  Options:
660
762
  --out-dir <dir> Output directory (default: <root>/dist)
661
763
  --conditions <list> Extra export conditions, comma separated
662
- --port <port> Simulator port for \`dev\`; the next free one is used if taken
663
- (default: ${DEFAULT_PORT})
764
+ --port <port> Simulator port for \`dev\` and \`preview\`; the next free one is used
765
+ if taken (default: ${DEFAULT_PORT})
664
766
  --device With \`dev\`, also serve the dot to the Dot example apps on phones on
665
767
  the local network, through a link and QR code; they reload on each build
666
768
  -h, --help Show this message`;
@@ -685,14 +787,14 @@ async function main(argv) {
685
787
  });
686
788
  const [command, target, pagePath] = positionals;
687
789
  if (command === "lsp" && !values.help) {
688
- const { serveStdio } = await import("./server-DFOUQnLA.js");
790
+ const { serveStdio } = await import("./server-BfPAnsSf.js");
689
791
  serveStdio();
690
792
  return new Promise(() => {});
691
793
  }
692
794
  if (command === "keys" && !values.help) return keys(target);
693
795
  if (command === "create" && target === "page" && !values.help) return addPage(pagePath);
694
796
  if (command === "create" && !values.help) return createApp(target, values.name, values.template, values.pm);
695
- if (values.help || command !== "build" && command !== "dev") {
797
+ if (values.help || command !== "build" && command !== "dev" && command !== "preview") {
696
798
  console.log(USAGE);
697
799
  return values.help ? 0 : 1;
698
800
  }
@@ -714,6 +816,19 @@ async function main(argv) {
714
816
  await stop();
715
817
  return 0;
716
818
  }
819
+ if (command === "preview") {
820
+ if (values.device) {
821
+ console.error("--device works only with dotctl dev");
822
+ return 1;
823
+ }
824
+ const simulator = await preview({
825
+ ...options,
826
+ port
827
+ });
828
+ await new Promise((resolve) => process.once("SIGINT", resolve));
829
+ await simulator.close();
830
+ return 0;
831
+ }
717
832
  const started = performance.now();
718
833
  const result = await build(options);
719
834
  for (const file of result.files.filter((file) => !file.endsWith(".map"))) console.log(` ${relative(process.cwd(), file)}`);
@@ -757,7 +872,7 @@ async function createApp(dir, name, templateFlag, packageManagerFlag) {
757
872
  console.error(`--pm must be one of ${PACKAGE_MANAGERS.join(", ")}`);
758
873
  return 1;
759
874
  }
760
- name ??= await ask("Dot name:", dir ? toDisplayName(dir) : "Dot");
875
+ name ??= await ask("Dot name", dir ? toDisplayName(dir) : "Dot");
761
876
  if (!name) {
762
877
  console.error("A dot needs a name: dotctl create [dir] --name \"My Dot\"");
763
878
  return 1;
@@ -772,7 +887,8 @@ async function createApp(dir, name, templateFlag, packageManagerFlag) {
772
887
  console.log(`\nCreated ${name} in ${path}`);
773
888
  if (workspace) console.log("It is inside the Dot repository, so it uses the workspace packages.");
774
889
  let packageManager = workspace ? "pnpm" : packageManagerFlag ?? launchedBy();
775
- if (await confirm("\nInstall and start it now?")) {
890
+ console.log();
891
+ if (await confirm("Install and start it now?", true)) {
776
892
  if (!workspace && !packageManagerFlag) packageManager = await choosePackageManager(packageManager);
777
893
  if (await run(packageManager, ["install"], root) === 0) return run(packageManager, ["run", "dev"], root);
778
894
  console.error(`\n${packageManager} install failed.`);
@@ -791,53 +907,24 @@ function run(command, args, cwd) {
791
907
  child.on("exit", (code) => resolvePromise(code ?? 1));
792
908
  });
793
909
  }
794
- async function confirm(question) {
795
- if (!process.stdin.isTTY) return false;
796
- const answer = await readAnswer(`${question} (Y/n)`, "y");
797
- return answer !== void 0 && answer.toLowerCase().startsWith("y");
798
- }
799
- async function ask(question, fallback) {
800
- return readAnswer(`${question}${fallback ? ` (${fallback})` : ""}`, fallback);
801
- }
802
- async function readAnswer(prompt, fallback) {
803
- if (!process.stdin.isTTY) return fallback;
804
- const readline = createInterface({
805
- input: process.stdin,
806
- output: process.stdout
807
- });
808
- try {
809
- return (await readline.question(`${prompt} `)).trim() || fallback;
810
- } catch (error) {
811
- if (error.name === "AbortError") return;
812
- throw error;
813
- } finally {
814
- readline.close();
815
- }
816
- }
817
- async function chooseTemplate() {
818
- if (!process.stdin.isTTY) return "starter";
819
- console.log("\nTemplates:\n starter one page to build on\n showcase a page for each feature, to read");
820
- return chooseOne("Template?", TEMPLATES, "starter");
910
+ function chooseTemplate() {
911
+ console.log();
912
+ return select("Template?", TEMPLATES.map((template) => ({
913
+ value: template,
914
+ hint: TEMPLATE_HINTS[template]
915
+ })), "starter");
821
916
  }
822
917
  async function choosePackageManager(suggested) {
823
918
  const installed = installedManagers();
824
919
  if (installed.length < 2) return installed[0] ?? suggested;
825
- return chooseOne("Package manager?", installed, installed.includes(suggested) ? suggested : installed[0]);
826
- }
827
- async function chooseOne(question, options, fallback) {
828
- while (true) {
829
- const answer = await ask(`${question} ${options.join(" / ")}`, fallback);
830
- if (answer === void 0) return fallback;
831
- const chosen = options.find((option) => option === answer);
832
- if (chosen) return chosen;
833
- console.log(`Choose one of ${options.join(", ")}.`);
834
- }
920
+ const fallback = installed.includes(suggested) ? suggested : installed[0];
921
+ return select("Package manager?", installed.map((manager) => ({ value: manager })), fallback);
835
922
  }
836
923
  main(process.argv.slice(2)).then((code) => process.exit(code), (error) => {
837
924
  console.error(`error: ${describeError(error)}`);
838
925
  process.exit(1);
839
926
  });
840
927
  //#endregion
841
- export {};
928
+ export { findDotRoot as t };
842
929
 
843
930
  //# sourceMappingURL=cli.js.map