create-seedcord 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ npm create seedcord my-bot
35
35
  yarn create seedcord my-bot
36
36
  ```
37
37
 
38
- It asks where the project goes, how Discord reaches your bot, what the bot should react to, your bot token, and an accent color. Then it writes the project, installs, formats, generates your command types, and makes the first commit.
38
+ It asks where the project goes, how Discord reaches your bot, what the bot should react to, your bot token, and an accent color. Then it writes the project, installs, formats, generates your command types, and makes the first commit. A step that fails after the write becomes a warning. The summary still prints.
39
39
 
40
40
  On Windows, run it from Windows Terminal. The prompts fall back to ASCII in `cmd.exe` making the boxes draw as `T`, `|`, and `o`.
41
41
 
@@ -50,9 +50,9 @@ npm create seedcord my-bot -- --transport gateway --capabilities reactions
50
50
  pnpm create seedcord my-bot --transport gateway --capabilities reactions
51
51
  ```
52
52
 
53
- `--no-install` and `--no-git` turn off those two steps.
53
+ `--no-install` and `--no-git` turn off those two steps. `-v` prints the version and `-h` prints the flag list.
54
54
 
55
- With no terminal to ask on, it reads the flags alone. It names the flag that would supply any answer you left out.
55
+ With no terminal to ask on, it reads the flags alone. It prints the flag that would supply any answer you left out.
56
56
 
57
57
  ## What you get
58
58
 
package/dist/index.mjs CHANGED
@@ -1922,7 +1922,7 @@ const chalk = createChalk();
1922
1922
  const chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
1923
1923
 
1924
1924
  //#endregion
1925
- //#region ../../packages/errors/dist/SeedcordError-C3HkJAhQ.mjs
1925
+ //#region ../../packages/errors/dist/SeedcordError-BsqYDjQD.mjs
1926
1926
  /**
1927
1927
  * All Seedcord error codes.
1928
1928
  */
@@ -2135,8 +2135,8 @@ let SeedcordErrorCode = /* @__PURE__ */ function(SeedcordErrorCode) {
2135
2135
  SeedcordErrorCode[SeedcordErrorCode["CliConfigNotFound"] = 3103] = "CliConfigNotFound";
2136
2136
  /** CLI entry file does not exist. */
2137
2137
  SeedcordErrorCode[SeedcordErrorCode["CliEntryNotFound"] = 3104] = "CliEntryNotFound";
2138
- /** tsx failed to import the provided entry file. */
2139
- SeedcordErrorCode[SeedcordErrorCode["CliTsxImportFailed"] = 3105] = "CliTsxImportFailed";
2138
+ /** Failed to import a TypeScript entry file. */
2139
+ SeedcordErrorCode[SeedcordErrorCode["CliTsImportFailed"] = 3105] = "CliTsImportFailed";
2140
2140
  /** Native import and jiti fallback both failed. */
2141
2141
  SeedcordErrorCode[SeedcordErrorCode["CliImportFailed"] = 3106] = "CliImportFailed";
2142
2142
  /** Seedcord instance export is missing a start() method. */
@@ -2322,7 +2322,7 @@ const messages = {
2322
2322
  [3109]: () => "Config must include an `entry` string that points to your startup script.",
2323
2323
  [3110]: (entryPath, root) => `Entry file ${entryPath} must reside inside configured root ${root}.`,
2324
2324
  [3104]: (entryPath) => `Cannot find entry file at ${entryPath}.`,
2325
- [3105]: (entryPath, reason) => `Failed to import ${entryPath} via tsx: ${reason}`,
2325
+ [3105]: (entryPath, reason) => `Failed to import TypeScript file ${entryPath}: ${reason}`,
2326
2326
  [3106]: (entryPath, nativeReason, fallbackReason) => `Failed to import ${entryPath}: ${nativeReason} (the jiti fallback also failed: ${fallbackReason})`,
2327
2327
  [3107]: () => "Seedcord instance must default export an object with a start() method.",
2328
2328
  [3108]: (instancePath, reason) => `Failed to start Seedcord from ${instancePath}: ${reason}`,
@@ -2465,7 +2465,7 @@ function isSeedcordError(error, type, code) {
2465
2465
 
2466
2466
  //#endregion
2467
2467
  //#region src/cli/banner.ts
2468
- function banner(version = "0.3.1") {
2468
+ function banner(version = "0.3.2") {
2469
2469
  const wordmark = `${paint.flesh.bold("seed")}${paint.rind.bold("cord")}${paint.mute(" create")}`;
2470
2470
  if (version === void 0) return wordmark;
2471
2471
  return `${wordmark}${paint.pith(` • v${version}`)}`;
@@ -2897,20 +2897,29 @@ const EXTRA = [
2897
2897
  name: "no-git",
2898
2898
  description: "skip git init and the first commit"
2899
2899
  },
2900
+ {
2901
+ name: "version",
2902
+ short: "v",
2903
+ description: "print the version"
2904
+ },
2900
2905
  {
2901
2906
  name: "help",
2907
+ short: "h",
2902
2908
  description: "print this"
2903
2909
  }
2904
2910
  ];
2911
+ function spelling(flag) {
2912
+ return flag.short === void 0 ? `--${flag.name}` : `-${flag.short}, --${flag.name}`;
2913
+ }
2905
2914
  function helpText() {
2906
2915
  const flags = [...STEPS.map((step) => step.flag), ...EXTRA];
2907
- const width = Math.max(...flags.map((flag) => flag.name.length));
2916
+ const width = Math.max(...flags.map((flag) => spelling(flag).length));
2908
2917
  return [
2909
2918
  "Usage: create-seedcord [directory] [flags]",
2910
2919
  "",
2911
2920
  "Every question the interview asks has a flag. Pass them all to skip the interview.",
2912
2921
  "",
2913
- ...flags.map((flag) => ` --${flag.name.padEnd(width)} ${flag.description}`),
2922
+ ...flags.map((flag) => ` ${spelling(flag).padEnd(width)} ${flag.description}`),
2914
2923
  "",
2915
2924
  "npm needs a -- before the flags: npm create seedcord my-bot -- --transport gateway"
2916
2925
  ].join("\n");
@@ -3550,9 +3559,13 @@ const OPTIONS = {
3550
3559
  "no-git": { type: "boolean" }
3551
3560
  };
3552
3561
  const HELP_FLAGS = /* @__PURE__ */ new Set(["--help", "-h"]);
3562
+ const VERSION_FLAGS = /* @__PURE__ */ new Set(["--version", "-v"]);
3553
3563
  function wantsHelp(argv) {
3554
3564
  return argv.some((argument) => HELP_FLAGS.has(argument));
3555
3565
  }
3566
+ function wantsVersion(argv) {
3567
+ return argv.some((argument) => VERSION_FLAGS.has(argument));
3568
+ }
3556
3569
  function readArgv(argv) {
3557
3570
  try {
3558
3571
  return parseArgs({
@@ -3594,11 +3607,10 @@ const CANCELLED = {
3594
3607
  };
3595
3608
  function reportFailure(error) {
3596
3609
  if (isSeedcordError(error, void 0, SeedcordErrorCode.CreateCancelled)) return CANCELLED;
3597
- const kept = isSeedcordError(error, void 0, SeedcordErrorCode.CreateStepFailed);
3598
3610
  return {
3599
3611
  code: 1,
3600
3612
  message: isSeedcordError(error) || Error.isError(error) ? error.message : String(error),
3601
- closing: kept ? "Project kept. Run the command manually." : "Nothing was created."
3613
+ closing: "Nothing was created."
3602
3614
  };
3603
3615
  }
3604
3616
 
@@ -3630,6 +3642,10 @@ function silentSteps() {
3630
3642
  };
3631
3643
  }
3632
3644
 
3645
+ //#endregion
3646
+ //#region src/cli/version.ts
3647
+ const version = "0.3.2";
3648
+
3633
3649
  //#endregion
3634
3650
  //#region src/cli/summary.ts
3635
3651
  const PORTAL = "https://discord.com/developers/applications";
@@ -3667,7 +3683,7 @@ function reproducingCommand(answers, agent) {
3667
3683
  `--color ${answers.botColor}`
3668
3684
  ];
3669
3685
  const separator = agent === "npm" ? "-- " : "";
3670
- return `${agent} create seedcord ${answers.directory} ${separator}${flags.join(" ")}`;
3686
+ return `${agent} create seedcord@${version} ${answers.directory} ${separator}${flags.join(" ")}`;
3671
3687
  }
3672
3688
 
3673
3689
  //#endregion
@@ -3705,17 +3721,33 @@ async function probeCloudflared(binary = "cloudflared", timeout = PROBE_TIMEOUT_
3705
3721
 
3706
3722
  //#endregion
3707
3723
  //#region src/scaffold/exec.ts
3708
- const KEPT_LINES = 12;
3709
- const NAMES_THE_CAUSE = /error|ERR!/i;
3724
+ const NAMES_THE_CAUSE = /error|ERR[_!]/i;
3725
+ const BLOCKED_BUILD = /ERR_PNPM_IGNORED_BUILDS/;
3726
+ const INDENTED = /^\s/;
3710
3727
  const LINE_BREAK = /[\r\n]+/;
3711
3728
  const PIPED_COLUMNS = 100;
3712
3729
  const GUTTER = 3;
3713
3730
  function lineWidth() {
3714
3731
  return (process$1.stdout.columns > 0 ? process$1.stdout.columns : PIPED_COLUMNS) - GUTTER;
3715
3732
  }
3733
+ function blockFrom(header, rest) {
3734
+ const detail = [];
3735
+ for (const line of rest) {
3736
+ if (!INDENTED.test(line)) break;
3737
+ detail.push(line);
3738
+ }
3739
+ return [header, ...detail];
3740
+ }
3716
3741
  function failureLines(captured) {
3717
- const named = captured.filter((line) => NAMES_THE_CAUSE.test(line));
3718
- return named.length > 0 ? named.slice(0, KEPT_LINES) : captured.slice(-12);
3742
+ const blocks = [];
3743
+ let consumedThrough = -1;
3744
+ for (const [index, line] of captured.entries()) {
3745
+ if (index <= consumedThrough || !NAMES_THE_CAUSE.test(line)) continue;
3746
+ const block = blockFrom(line, captured.slice(index + 1));
3747
+ blocks.push(...block);
3748
+ consumedThrough = index + block.length - 1;
3749
+ }
3750
+ return (blocks.length > 0 ? blocks : captured).slice(-12);
3719
3751
  }
3720
3752
  const SHIMS = /* @__PURE__ */ new Set([
3721
3753
  "npm",
@@ -3750,8 +3782,8 @@ async function execRunner(...[command, args, cwd]) {
3750
3782
  const captured = [];
3751
3783
  const take = (chunk) => {
3752
3784
  for (const line of chunk.toString("utf8").split(LINE_BREAK)) {
3753
- const text = line.trim().slice(0, width);
3754
- if (text === "") continue;
3785
+ const text = line.trimEnd().slice(0, width);
3786
+ if (text.trim() === "") continue;
3755
3787
  captured.push(text);
3756
3788
  }
3757
3789
  };
@@ -3762,10 +3794,14 @@ async function execRunner(...[command, args, cwd]) {
3762
3794
  });
3763
3795
  child.on("close", (code) => {
3764
3796
  if (code === 0) {
3765
- resolve();
3797
+ resolve(null);
3766
3798
  return;
3767
3799
  }
3768
3800
  const shown = failureLines(captured).join("\n");
3801
+ if (captured.some((line) => BLOCKED_BUILD.test(line))) {
3802
+ resolve(shown);
3803
+ return;
3804
+ }
3769
3805
  reject(new SeedcordError(SeedcordErrorCode.CreateStepFailed, [spoken, shown || `exited with ${code}`]));
3770
3806
  });
3771
3807
  });
@@ -11117,10 +11153,25 @@ async function renderTemplates(templatesRoot, context) {
11117
11153
 
11118
11154
  //#endregion
11119
11155
  //#region src/scaffold/scaffold.ts
11156
+ const INSTALL_STEPS = {
11157
+ install: {
11158
+ running: "Installing dependencies",
11159
+ done: "Dependencies installed"
11160
+ },
11161
+ format: {
11162
+ running: "Formatting",
11163
+ done: "Code formatted"
11164
+ },
11165
+ codegen: {
11166
+ running: "Generating types",
11167
+ done: "Types generated"
11168
+ }
11169
+ };
11120
11170
  const DEV_PACKAGES = [
11121
11171
  "@seedcord/eslint-config",
11122
11172
  "@seedcord/tsconfig",
11123
11173
  "@types/node",
11174
+ "eslint",
11124
11175
  "jiti",
11125
11176
  "prettier",
11126
11177
  "seedcord",
@@ -11137,25 +11188,42 @@ async function writeTree(target, files) {
11137
11188
  await writeFile(destination, file.contents, "utf8");
11138
11189
  }
11139
11190
  }
11140
- async function runGitSteps(input, run) {
11191
+ async function runStep(steps, labels, work) {
11141
11192
  try {
11142
- await input.steps.run({
11143
- running: "Setting up git",
11144
- done: "Committed"
11145
- }, async () => {
11146
- await run("git", ["init"], input.target);
11147
- await run("git", ["add", "."], input.target);
11148
- await run("git", [
11149
- "commit",
11150
- "-m",
11151
- "chore: create seedcord bot"
11152
- ], input.target);
11153
- });
11193
+ await steps.run(labels, work);
11154
11194
  return null;
11155
11195
  } catch (error) {
11156
- return `${Error.isError(error) ? error.message : String(error)} The project is complete and uncommitted.`;
11196
+ return Error.isError(error) ? error.message : String(error);
11157
11197
  }
11158
11198
  }
11199
+ async function gitNoticeFor(input, run, plan) {
11200
+ if (!plan.init) {
11201
+ input.steps.skip("Committed");
11202
+ return plan.notice;
11203
+ }
11204
+ const reason = await runStep(input.steps, {
11205
+ running: "Setting up git",
11206
+ done: "Committed"
11207
+ }, async () => {
11208
+ await run("git", ["init"], input.target);
11209
+ await run("git", ["add", "."], input.target);
11210
+ await run("git", [
11211
+ "commit",
11212
+ "-m",
11213
+ "chore: create seedcord bot"
11214
+ ], input.target);
11215
+ });
11216
+ if (reason === null) return plan.notice;
11217
+ return `${reason} The project is complete and uncommitted.`;
11218
+ }
11219
+ function skipInstallSteps(steps) {
11220
+ for (const labels of Object.values(INSTALL_STEPS)) steps.skip(labels.done);
11221
+ return {
11222
+ installed: false,
11223
+ notices: [],
11224
+ failed: false
11225
+ };
11226
+ }
11159
11227
  async function runInstallSteps(input, run, isGateway) {
11160
11228
  const { agent, steps, target } = input;
11161
11229
  const deps = addCommand(agent, runtimePackages(isGateway), false);
@@ -11166,66 +11234,67 @@ async function runInstallSteps(input, run, isGateway) {
11166
11234
  "."
11167
11235
  ]);
11168
11236
  const codegen = execCommand(agent, ["seedcord", "codegen"]);
11169
- await steps.run({
11170
- running: "Installing dependencies",
11171
- done: "Dependencies installed"
11172
- }, async () => {
11173
- await run(deps.command, deps.args, target);
11174
- await run(dev.command, dev.args, target);
11237
+ const warnings = [];
11238
+ const collect = (warning) => {
11239
+ if (warning !== null) warnings.push(warning);
11240
+ };
11241
+ const install = await runStep(steps, INSTALL_STEPS.install, async () => {
11242
+ collect(await run(deps.command, deps.args, target));
11243
+ collect(await run(dev.command, dev.args, target));
11175
11244
  });
11176
- await steps.run({
11177
- running: "Formatting",
11178
- done: "Code formatted"
11179
- }, () => run(format.command, format.args, target));
11180
- await steps.run({
11181
- running: "Generating types",
11182
- done: "Types generated"
11183
- }, () => run(codegen.command, codegen.args, target));
11245
+ if (install !== null) {
11246
+ steps.skip(INSTALL_STEPS.format.done);
11247
+ steps.skip(INSTALL_STEPS.codegen.done);
11248
+ return {
11249
+ installed: false,
11250
+ notices: [install, ...warnings],
11251
+ failed: true
11252
+ };
11253
+ }
11254
+ const failures = [await runStep(steps, INSTALL_STEPS.format, () => run(format.command, format.args, target)), await runStep(steps, INSTALL_STEPS.codegen, () => run(codegen.command, codegen.args, target))].filter((reason) => reason !== null);
11255
+ return {
11256
+ installed: true,
11257
+ notices: [...warnings, ...failures],
11258
+ failed: failures.length > 0
11259
+ };
11184
11260
  }
11185
- async function scaffold(input, run) {
11186
- const { existed } = await claimTarget(input.target);
11187
- const parent = dirname(input.target);
11188
- await mkdir(parent, { recursive: true });
11189
- const plan = gitPlanFrom(await probeGit(parent), input.git);
11190
- let written = false;
11261
+ async function writeProject(input, plan, existed) {
11262
+ const context = buildContext(input.answers, {
11263
+ developerUsername: plan.developerUsername,
11264
+ runCommand: runPrefix(input.agent)
11265
+ });
11191
11266
  try {
11192
- const context = buildContext(input.answers, {
11193
- developerUsername: plan.developerUsername,
11194
- runCommand: runPrefix(input.agent)
11195
- });
11196
- const { steps } = input;
11197
- await steps.run({
11267
+ await input.steps.run({
11198
11268
  running: "Writing files",
11199
11269
  done: "Files written"
11200
11270
  }, async () => {
11201
11271
  await mkdir(input.target, { recursive: true });
11202
11272
  await writeTree(input.target, await renderTemplates(input.templatesRoot, context));
11203
11273
  });
11204
- written = true;
11205
- if (input.install) await runInstallSteps(input, run, context.isGateway);
11206
- else {
11207
- steps.skip("Dependencies installed");
11208
- steps.skip("Code formatted");
11209
- steps.skip("Types generated");
11210
- }
11211
- let gitNotice = plan.notice;
11212
- if (plan.init) gitNotice = await runGitSteps(input, run) ?? gitNotice;
11213
- else steps.skip("Committed");
11214
- return {
11215
- installed: input.install,
11216
- gitNotice
11217
- };
11274
+ return context;
11218
11275
  } catch (error) {
11219
- if (!written) {
11220
- await rm(input.target, {
11221
- recursive: true,
11222
- force: true
11223
- });
11224
- if (existed) await mkdir(input.target, { recursive: true });
11225
- }
11276
+ await rm(input.target, {
11277
+ recursive: true,
11278
+ force: true
11279
+ });
11280
+ if (existed) await mkdir(input.target, { recursive: true });
11226
11281
  throw error;
11227
11282
  }
11228
11283
  }
11284
+ async function scaffold(input, run) {
11285
+ const { existed } = await claimTarget(input.target);
11286
+ const parent = dirname(input.target);
11287
+ await mkdir(parent, { recursive: true });
11288
+ const plan = gitPlanFrom(await probeGit(parent), input.git);
11289
+ const context = await writeProject(input, plan, existed);
11290
+ const outcome = input.install ? await runInstallSteps(input, run, context.isGateway) : skipInstallSteps(input.steps);
11291
+ const gitNotice = await gitNoticeFor(input, run, plan);
11292
+ return {
11293
+ installed: outcome.installed,
11294
+ notices: gitNotice === null ? outcome.notices : [...outcome.notices, gitNotice],
11295
+ failed: outcome.failed
11296
+ };
11297
+ }
11229
11298
 
11230
11299
  //#endregion
11231
11300
  //#region src/index.ts
@@ -11242,6 +11311,10 @@ async function main() {
11242
11311
  process$1.stdout.write(`${helpText()}\n`);
11243
11312
  return;
11244
11313
  }
11314
+ if (wantsVersion(argv)) {
11315
+ process$1.stdout.write(`${version}\n`);
11316
+ return;
11317
+ }
11245
11318
  const interactive = isInteractive();
11246
11319
  if (interactive) intro(banner());
11247
11320
  const input = parseInput(argv);
@@ -11257,8 +11330,9 @@ async function main() {
11257
11330
  git: input.git,
11258
11331
  steps: interactive ? clackSteps() : silentSteps()
11259
11332
  }, execRunner);
11260
- if (result.gitNotice !== null) log.warn(result.gitNotice);
11333
+ for (const notice of result.notices) log.warn(notice);
11261
11334
  await reportOutcome(answers, agent, result.installed, interactive);
11335
+ if (result.failed) process$1.exitCode = 1;
11262
11336
  }
11263
11337
  function copyable(label, value) {
11264
11338
  log.message(paint.mute(label));