@bigcommerce/create-catalyst 0.20.0 → 0.21.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.
package/dist/index.js CHANGED
@@ -53,12 +53,12 @@ var __async = (__this, __arguments, generator) => {
53
53
 
54
54
  // src/index.ts
55
55
  import { program } from "@commander-js/extra-typings";
56
- import chalk6 from "chalk";
56
+ import chalk7 from "chalk";
57
57
 
58
58
  // package.json
59
59
  var package_default = {
60
60
  name: "@bigcommerce/create-catalyst",
61
- version: "0.20.0",
61
+ version: "0.21.0",
62
62
  type: "module",
63
63
  bin: "bin/index.cjs",
64
64
  files: [
@@ -76,18 +76,23 @@ var package_default = {
76
76
  node: ">=20.0.0"
77
77
  },
78
78
  dependencies: {
79
- "@commander-js/extra-typings": "^12.1.0",
79
+ "@commander-js/extra-typings": "^13.1.0",
80
80
  "@iarna/toml": "^2.2.5",
81
- "@inquirer/prompts": "^7.2.0",
82
- "@segment/analytics-node": "^2.2.0",
83
- chalk: "^5.3.0",
84
- commander: "^12.1.0",
81
+ "@inquirer/core": "^10.1.6",
82
+ "@inquirer/figures": "^1.0.10",
83
+ "@inquirer/prompts": "^7.2.3",
84
+ "@inquirer/type": "^3.0.4",
85
+ "@segment/analytics-node": "^2.2.1",
86
+ "ansi-escapes": "^7.0.0",
87
+ chalk: "^5.4.1",
88
+ commander: "^13.1.0",
85
89
  conf: "^13.1.0",
90
+ "cross-spawn": "^7.0.6",
86
91
  dotenv: "^16.4.7",
87
- "fs-extra": "^11.2.0",
92
+ "fs-extra": "^11.3.0",
88
93
  giget: "^1.2.3",
89
94
  "lodash.kebabcase": "^4.1.1",
90
- nypm: "^0.4.1",
95
+ nypm: "^0.5.0",
91
96
  open: "^10.1.0",
92
97
  ora: "^8.1.1",
93
98
  semver: "^7.6.3",
@@ -98,32 +103,249 @@ var package_default = {
98
103
  devDependencies: {
99
104
  "@bigcommerce/eslint-config": "^2.10.0",
100
105
  "@bigcommerce/eslint-config-catalyst": "workspace:^",
101
- "@swc/core": "^1.10.1",
106
+ "@swc/core": "^1.10.11",
102
107
  "@swc/jest": "^0.2.37",
108
+ "@types/cross-spawn": "^6.0.6",
103
109
  "@types/fs-extra": "^11.0.4",
104
110
  "@types/jest": "^29.5.14",
105
111
  "@types/lodash.kebabcase": "^4.1.9",
106
- "@types/node": "^20.17.10",
112
+ "@types/node": "^20.17.16",
107
113
  "@types/prompts": "^2.4.9",
108
114
  "@types/semver": "^7.5.8",
109
115
  eslint: "^8.57.1",
110
116
  jest: "^29.7.0",
111
- msw: "^2.6.9",
117
+ msw: "^2.7.0",
112
118
  prettier: "^3.4.2",
113
- tsup: "^8.3.5",
114
- typescript: "^5.7.2"
119
+ tsup: "^8.3.6",
120
+ typescript: "^5.7.3"
115
121
  }
116
122
  };
117
123
 
118
124
  // src/commands/create.ts
119
125
  import { Command, Option } from "@commander-js/extra-typings";
120
126
  import { input, select } from "@inquirer/prompts";
121
- import chalk3 from "chalk";
122
- import { execSync as execSync5 } from "child_process";
127
+ import chalk4 from "chalk";
128
+ import { execSync as execSync3 } from "child_process";
123
129
  import { pathExistsSync } from "fs-extra/esm";
124
130
  import kebabCase from "lodash.kebabcase";
125
131
  import { join as join3 } from "path";
126
132
 
133
+ // src/prompts/multi-select/multi-select.ts
134
+ import {
135
+ createPrompt,
136
+ isBackspaceKey,
137
+ isDownKey,
138
+ isEnterKey,
139
+ isNumberKey,
140
+ isSpaceKey,
141
+ isUpKey,
142
+ makeTheme,
143
+ Separator as Separator2,
144
+ useEffect,
145
+ useKeypress,
146
+ useMemo,
147
+ usePagination,
148
+ usePrefix,
149
+ useRef,
150
+ useState,
151
+ ValidationError
152
+ } from "@inquirer/core";
153
+ import ansiEscapes from "ansi-escapes";
154
+
155
+ // src/prompts/multi-select/helpers.ts
156
+ import { Separator } from "@inquirer/core";
157
+ import figures from "@inquirer/figures";
158
+ import chalk from "chalk";
159
+ var selectTheme = {
160
+ helpMode: "auto",
161
+ icon: {
162
+ checked: chalk.green(figures.circleFilled),
163
+ unchecked: figures.circle,
164
+ cursor: figures.pointer
165
+ },
166
+ style: {
167
+ description: (text) => chalk.cyan(text),
168
+ disabledChoice: (text) => chalk.dim(`- ${text}`),
169
+ renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", ")
170
+ }
171
+ };
172
+ var isSelectable = (item) => !Separator.isSeparator(item) && !item.disabled;
173
+ var isChecked = (item) => isSelectable(item) && Boolean(item.checked);
174
+ var toggle = (item) => isSelectable(item) ? __spreadProps(__spreadValues({}, item), { checked: !item.checked }) : item;
175
+ var normalizeChoices = (choices) => choices.map((choice) => {
176
+ var _a, _b, _c, _d;
177
+ if (Separator.isSeparator(choice)) {
178
+ return choice;
179
+ }
180
+ if (typeof choice === "string") {
181
+ return {
182
+ checked: false,
183
+ disabled: false,
184
+ name: choice,
185
+ short: choice,
186
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
187
+ value: choice
188
+ };
189
+ }
190
+ const name2 = (_a = choice.name) != null ? _a : String(choice.value);
191
+ return {
192
+ checked: (_b = choice.checked) != null ? _b : false,
193
+ description: choice.description,
194
+ disabled: (_c = choice.disabled) != null ? _c : false,
195
+ name: name2,
196
+ short: (_d = choice.short) != null ? _d : name2,
197
+ value: choice.value
198
+ };
199
+ });
200
+
201
+ // src/prompts/multi-select/multi-select.ts
202
+ import { Separator as Separator3 } from "@inquirer/core";
203
+ var multiSelect = createPrompt(
204
+ (config, done) => {
205
+ const theme = makeTheme(selectTheme, config.theme);
206
+ const searchTimeoutRef = useRef();
207
+ const firstRender = useRef(true);
208
+ const [items, setItems] = useState(
209
+ normalizeChoices(config.choices)
210
+ );
211
+ const [status, setStatus] = useState("idle");
212
+ const [errorMsg, setError] = useState();
213
+ const prefix = usePrefix({ status, theme });
214
+ const { instructions, loop = true, pageSize = 7, required, validate = () => true } = config;
215
+ const bounds = useMemo(() => {
216
+ const first = items.findIndex(isSelectable);
217
+ const last = items.findLastIndex(isSelectable);
218
+ if (first === -1) {
219
+ throw new ValidationError(
220
+ "[multi select prompt] No selectable choices. All choices are disabled."
221
+ );
222
+ }
223
+ return { first, last };
224
+ }, [items]);
225
+ const [showHelpTip, setShowHelpTip] = useState(true);
226
+ const [active, setActive] = useState(bounds.first);
227
+ useEffect(
228
+ () => () => {
229
+ clearTimeout(searchTimeoutRef.current);
230
+ },
231
+ []
232
+ );
233
+ useKeypress((key, rl) => __async(void 0, null, function* () {
234
+ clearTimeout(searchTimeoutRef.current);
235
+ if (isEnterKey(key)) {
236
+ const selection = items.filter(isChecked);
237
+ const isValid = yield validate([...selection]);
238
+ if (required && !items.some(isChecked)) {
239
+ setError("At least one choice must be selected");
240
+ } else if (isValid === true) {
241
+ setStatus("done");
242
+ done(selection.map((choice) => choice.value));
243
+ } else {
244
+ setError(isValid || "You must select a valid value");
245
+ }
246
+ } else if (isUpKey(key) || isDownKey(key)) {
247
+ rl.clearLine(0);
248
+ if (loop || isUpKey(key) && active !== bounds.first || isDownKey(key) && active !== bounds.last) {
249
+ const offset = isUpKey(key) ? -1 : 1;
250
+ let next = active;
251
+ do {
252
+ next = (next + offset + items.length) % items.length;
253
+ } while (!isSelectable(items[next]));
254
+ setActive(next);
255
+ }
256
+ } else if (isNumberKey(key)) {
257
+ rl.clearLine(0);
258
+ const position = Number(key.name) - 1;
259
+ if (isSelectable(items[position])) {
260
+ setActive(position);
261
+ }
262
+ } else if (isBackspaceKey(key)) {
263
+ rl.clearLine(0);
264
+ } else if (isSpaceKey(key)) {
265
+ rl.clearLine(0);
266
+ setShowHelpTip(false);
267
+ const nextItems = items.map((choice, i) => i === active ? toggle(choice) : choice);
268
+ const selection = nextItems.filter(isChecked);
269
+ const isValid = yield validate([...selection]);
270
+ if (isValid === true) {
271
+ setError(void 0);
272
+ setItems(nextItems);
273
+ } else {
274
+ setError(isValid || "You must select a valid value");
275
+ }
276
+ } else {
277
+ const searchTerm = rl.line.toLowerCase();
278
+ const matchIndex = items.findIndex(
279
+ (item) => Separator2.isSeparator(item) || !isSelectable(item) ? false : item.name.toLowerCase().startsWith(searchTerm)
280
+ );
281
+ if (matchIndex !== -1) {
282
+ setActive(matchIndex);
283
+ }
284
+ searchTimeoutRef.current = setTimeout(() => {
285
+ rl.clearLine(0);
286
+ }, 700);
287
+ }
288
+ }));
289
+ const message = theme.style.message(config.message, status);
290
+ let helpTipTop = "";
291
+ let helpTipBottom = "";
292
+ let description;
293
+ if (theme.helpMode === "always" || theme.helpMode === "auto" && showHelpTip && (instructions === void 0 || instructions)) {
294
+ if (typeof instructions === "string") {
295
+ helpTipTop = instructions;
296
+ } else {
297
+ const keys = [
298
+ `${theme.style.key("space")} to select`,
299
+ `${theme.style.key("enter")} to proceed`
300
+ ];
301
+ helpTipTop = ` (Press ${keys.filter((key) => key !== "").join(", ")})`;
302
+ }
303
+ if (items.length > pageSize && (theme.helpMode === "always" || // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
304
+ theme.helpMode === "auto" && firstRender.current)) {
305
+ helpTipBottom = `
306
+ ${theme.style.help("(Use arrow keys to reveal more choices)")}`;
307
+ firstRender.current = false;
308
+ }
309
+ }
310
+ const page = usePagination({
311
+ items,
312
+ active,
313
+ renderItem({ item, isActive }) {
314
+ if (Separator2.isSeparator(item)) {
315
+ return ` ${item.separator}`;
316
+ }
317
+ if (item.disabled) {
318
+ return theme.style.disabledChoice(
319
+ `${item.name} ${typeof item.disabled === "string" ? item.disabled : "(disabled)"}`
320
+ );
321
+ }
322
+ if (isActive) {
323
+ description = item.description;
324
+ }
325
+ const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
326
+ const color = isActive ? theme.style.highlight : (x) => x;
327
+ const cursor = isActive ? theme.icon.cursor : ` `;
328
+ return color(`${cursor}${checkbox} ${item.name}`);
329
+ },
330
+ pageSize,
331
+ loop
332
+ });
333
+ if (status === "done") {
334
+ return `${prefix} ${message} ${theme.style.answer(theme.style.renderSelectedChoices(items.filter(isChecked), items))}`;
335
+ }
336
+ const selectedChoices = items.filter(isChecked).length > 0 ? ` (${theme.style.answer(theme.style.renderSelectedChoices(items.filter(isChecked), items))})` : "";
337
+ const choiceDescription = description ? `
338
+ ${theme.style.description(description)}` : ``;
339
+ let error = "";
340
+ if (errorMsg) {
341
+ error = `
342
+ ${theme.style.error(errorMsg)}`;
343
+ }
344
+ return `${prefix} ${message}${selectedChoices}${helpTipTop}
345
+ ${page}${helpTipBottom}${choiceDescription}${error}${ansiEscapes.cursorHide}`;
346
+ }
347
+ );
348
+
127
349
  // src/utils/user-agent.ts
128
350
  import {
129
351
  isDevelopment,
@@ -184,7 +406,7 @@ var Https = class {
184
406
  return __async(this, arguments, function* (path, opts = {}) {
185
407
  const _a = opts, { headers = {} } = _a, rest = __objRest(_a, ["headers"]);
186
408
  const options = __spreadValues({
187
- headers: __spreadValues(__spreadProps(__spreadValues({}, headers), {
409
+ headers: __spreadValues(__spreadProps(__spreadValues({}, Object.fromEntries(new Headers(headers))), {
188
410
  Accept: "application/json",
189
411
  "User-Agent": this.userAgent
190
412
  }), this.accessToken && { "X-Auth-Token": this.accessToken })
@@ -216,7 +438,7 @@ var CliApi = class {
216
438
  });
217
439
  });
218
440
  }
219
- createChannel(name2, installSampleData = false) {
441
+ createChannel(name2, storefrontLocale, additionalLocales, installSampleData = false) {
220
442
  return __async(this, null, function* () {
221
443
  return this.client.fetch("/channels/catalyst", {
222
444
  method: "POST",
@@ -227,7 +449,9 @@ var CliApi = class {
227
449
  type: installSampleData ? "sample" : "none"
228
450
  },
229
451
  deployStorefront: true,
230
- devOrigin: "http://localhost:3000"
452
+ devOrigin: "http://localhost:3000",
453
+ storefrontLanguage: storefrontLocale,
454
+ additionalLocales
231
455
  })
232
456
  });
233
457
  });
@@ -235,10 +459,10 @@ var CliApi = class {
235
459
  };
236
460
 
237
461
  // src/utils/clone-catalyst.ts
238
- import { execSync as execSync4 } from "child_process";
462
+ import { execSync as execSync2 } from "child_process";
239
463
 
240
464
  // src/utils/checkout-ref.ts
241
- import { execSync } from "node:child_process";
465
+ import { sync as spawnSync } from "cross-spawn";
242
466
 
243
467
  // src/utils/is-exec-exception.ts
244
468
  function isExecException(error) {
@@ -248,11 +472,17 @@ function isExecException(error) {
248
472
  // src/utils/checkout-ref.ts
249
473
  function checkoutRef(repoDir, ref) {
250
474
  try {
251
- execSync(`git checkout ${ref}`, {
475
+ const spawn = spawnSync("git", ["checkout", ref, "--"], {
252
476
  cwd: repoDir,
253
- stdio: "inherit",
254
- encoding: "utf8"
477
+ encoding: "utf8",
478
+ // Explicitly set shell to false to avoid shell injection
479
+ // Don't use shell: true as it's a security risk
480
+ shell: false
255
481
  });
482
+ const stderr = spawn.stderr.trim();
483
+ if (spawn.status !== 0 && stderr) {
484
+ throw new Error(stderr);
485
+ }
256
486
  console.log(`Checked out ref ${ref} successfully.`);
257
487
  } catch (error) {
258
488
  if (isExecException(error)) {
@@ -262,19 +492,20 @@ function checkoutRef(repoDir, ref) {
262
492
  } else {
263
493
  console.error(`Error checking out ref '${ref}':`, stderr.trim());
264
494
  }
265
- }
266
- if (error instanceof Error) {
495
+ } else if (error instanceof Error) {
267
496
  console.error(`Error checking out ref '${ref}':`, error.message);
497
+ } else {
498
+ console.error(`Unknown error occurred while checking out ref '${ref}'.`);
268
499
  }
269
- console.error(`Unknown error occurred while checking out ref '${ref}'.`);
500
+ console.warn(`Falling back to the default branch.`);
270
501
  }
271
502
  }
272
503
 
273
504
  // src/utils/has-github-ssh.ts
274
- import { execSync as execSync2 } from "child_process";
505
+ import { execSync } from "child_process";
275
506
  function hasGitHubSSH() {
276
507
  try {
277
- const output = execSync2("ssh -T git@github.com", {
508
+ const output = execSync("ssh -T git@github.com", {
278
509
  encoding: "utf8",
279
510
  stdio: "pipe"
280
511
  }).toString();
@@ -293,13 +524,19 @@ function hasGitHubSSH() {
293
524
  }
294
525
 
295
526
  // src/utils/reset-branch-to-ref.ts
296
- import { execSync as execSync3 } from "node:child_process";
527
+ import { sync as spawnSync2 } from "cross-spawn";
297
528
  function resetBranchToRef(projectDir, ghRef) {
298
- execSync3(`git reset --hard ${ghRef}`, {
529
+ const spawn = spawnSync2("git", ["reset", "--hard", ghRef, "--"], {
299
530
  cwd: projectDir,
300
- stdio: "inherit",
301
- encoding: "utf8"
531
+ encoding: "utf8",
532
+ // Explicitly set shell to false to avoid shell injection
533
+ // Don't use shell: true as it's a security risk
534
+ shell: false
302
535
  });
536
+ const stderr = spawn.stderr.trim();
537
+ if (spawn.status !== 0 && stderr) {
538
+ throw new Error(stderr);
539
+ }
303
540
  }
304
541
 
305
542
  // src/utils/clone-catalyst.ts
@@ -314,16 +551,16 @@ var cloneCatalyst = ({
314
551
  console.log(`Cloning ${repository} using ${useSSH ? "SSH" : "HTTPS"}...
315
552
  `);
316
553
  const cloneCommand = `git clone ${useSSH ? `git@github.com:${repository}` : `https://github.com/${repository}`}.git${projectName ? ` ${projectName}` : ""}`;
317
- execSync4(cloneCommand, { stdio: "inherit" });
554
+ execSync2(cloneCommand, { stdio: "inherit" });
318
555
  console.log();
319
- execSync4("git remote rename origin upstream", { cwd: projectDir, stdio: "inherit" });
556
+ execSync2("git remote rename origin upstream", { cwd: projectDir, stdio: "inherit" });
320
557
  console.log();
321
558
  if (ghRef) {
322
559
  if (resetMain) {
323
- execSync4("git checkout -b main", { cwd: projectDir, stdio: "inherit" });
560
+ execSync2("git checkout -b main", { cwd: projectDir, stdio: "inherit" });
324
561
  resetBranchToRef(projectDir, ghRef);
325
- console.log(`Reset main to ${ghRef} successfully.`);
326
- console.log();
562
+ console.log(`Reset main to ${ghRef} successfully.
563
+ `);
327
564
  return;
328
565
  }
329
566
  checkoutRef(projectDir, ghRef);
@@ -332,7 +569,7 @@ var cloneCatalyst = ({
332
569
  };
333
570
 
334
571
  // src/utils/install-dependencies.ts
335
- import chalk from "chalk";
572
+ import chalk2 from "chalk";
336
573
  import { installDependencies as installDeps } from "nypm";
337
574
 
338
575
  // src/utils/spinner.ts
@@ -351,26 +588,68 @@ var installDependencies = (projectDir) => __async(void 0, null, function* () {
351
588
  return spinner(installAllDeps(projectDir), {
352
589
  text: `Installing dependencies. This could take a minute...`,
353
590
  successText: `Dependencies installed successfully`,
354
- failText: (err) => chalk.red(`Failed to install dependencies: ${err.message}`)
591
+ failText: (err) => chalk2.red(`Failed to install dependencies: ${err.message}`)
355
592
  });
356
593
  });
357
594
 
595
+ // src/utils/localization.ts
596
+ import { z } from "zod";
597
+ var allowedLocales = [
598
+ "en",
599
+ "da",
600
+ "es-AR",
601
+ "es-CL",
602
+ "es-CO",
603
+ "es-MX",
604
+ "es-PE",
605
+ "es",
606
+ "it",
607
+ "nl",
608
+ "pl",
609
+ "pt",
610
+ "de",
611
+ "fr",
612
+ "ja",
613
+ "no",
614
+ "pt-BR",
615
+ "sv"
616
+ ];
617
+ var AvailableLocalesSuccessSchema = z.object({
618
+ data: z.array(
619
+ z.object({
620
+ id: z.string(),
621
+ name: z.string(),
622
+ fallback: z.string().nullable(),
623
+ is_supported: z.boolean()
624
+ })
625
+ )
626
+ });
627
+ var getAvailableLocales = (bc) => __async(void 0, null, function* () {
628
+ const response = yield bc.fetch("/v3/settings/store/available-locales");
629
+ if (!response.ok) {
630
+ throw new Error(
631
+ `GET /v3/settings/store/available-locales failed: ${response.status} ${response.statusText}`
632
+ );
633
+ }
634
+ return AvailableLocalesSuccessSchema.parse(yield response.json()).data.filter(({ id }) => allowedLocales.includes(id)).map(({ name: name2, id }) => ({ name: `${name2} (${id})`, value: id }));
635
+ });
636
+
358
637
  // src/utils/login.ts
359
- import chalk2 from "chalk";
638
+ import chalk3 from "chalk";
360
639
  import open from "open";
361
640
  import { createInterface } from "readline";
362
641
 
363
642
  // src/utils/auth.ts
364
- import { z as z2 } from "zod";
643
+ import { z as z3 } from "zod";
365
644
 
366
645
  // src/utils/parse.ts
367
- import * as z from "zod";
646
+ import * as z2 from "zod";
368
647
  import { fromZodError } from "zod-validation-error";
369
648
  var parse = (data, schema) => {
370
649
  try {
371
650
  return schema.parse(data);
372
651
  } catch (error) {
373
- if (error instanceof z.ZodError) {
652
+ if (error instanceof z2.ZodError) {
374
653
  console.error(fromZodError(error).toString());
375
654
  }
376
655
  process.exit(1);
@@ -401,12 +680,12 @@ var Auth = class {
401
680
  client_id: this.DEVICE_OAUTH_CLIENT_ID
402
681
  })
403
682
  });
404
- const DeviceCodeSchema = z2.object({
405
- device_code: z2.string(),
406
- user_code: z2.string(),
407
- verification_uri: z2.string(),
408
- expires_in: z2.number(),
409
- interval: z2.number()
683
+ const DeviceCodeSchema = z3.object({
684
+ device_code: z3.string(),
685
+ user_code: z3.string(),
686
+ verification_uri: z3.string(),
687
+ expires_in: z3.number(),
688
+ interval: z3.number()
410
689
  });
411
690
  return parse(yield response.json(), DeviceCodeSchema);
412
691
  });
@@ -424,11 +703,11 @@ var Auth = class {
424
703
  if (response.status !== 200) {
425
704
  throw new Error("Device code not yet verified");
426
705
  }
427
- const DeviceCodeSuccessSchema = z2.object({
428
- access_token: z2.string(),
429
- store_hash: z2.string(),
430
- context: z2.string(),
431
- api_uri: z2.string().url()
706
+ const DeviceCodeSuccessSchema = z3.object({
707
+ access_token: z3.string(),
708
+ store_hash: z3.string(),
709
+ context: z3.string(),
710
+ api_uri: z3.string().url()
432
711
  });
433
712
  return parse(yield response.json(), DeviceCodeSuccessSchema);
434
713
  });
@@ -555,7 +834,7 @@ function login(baseUrl) {
555
834
  const auth = new Auth({ baseUrl });
556
835
  const deviceCode = yield auth.getDeviceCode();
557
836
  console.log(
558
- chalk2.yellow("\n! First copy your one-time code: ") + chalk2.bold(deviceCode.user_code)
837
+ chalk3.yellow("\n! First copy your one-time code: ") + chalk3.bold(deviceCode.user_code)
559
838
  );
560
839
  console.log(`Press Enter to open ${deviceCode.verification_uri} in your browser...`);
561
840
  const shouldOpenUrl = yield waitForKeyPress("");
@@ -679,11 +958,55 @@ function getPlatformCheckCommand(command) {
679
958
  return isWindows2 ? `where.exe ${command}` : `which ${command}`;
680
959
  }
681
960
  var telemetry = new Telemetry();
682
- function handleChannelCreation(cliApi) {
961
+ function handleChannelCreation(bc, cliApi) {
683
962
  return __async(this, null, function* () {
684
963
  const newChannelName = yield input({
685
964
  message: "What would you like to name your new channel?"
686
965
  });
966
+ let availableLocales = [];
967
+ try {
968
+ availableLocales = yield getAvailableLocales(bc);
969
+ } catch (error) {
970
+ if (error instanceof Error) {
971
+ console.error(chalk4.red(error.message));
972
+ }
973
+ process.exit(1);
974
+ }
975
+ const storefrontLocale = yield select({
976
+ message: "Which default language would you like to set for your channel?",
977
+ default: "en",
978
+ choices: availableLocales,
979
+ theme: {
980
+ style: {
981
+ help: () => chalk4.dim("(Select locale from the list or start typing the name)")
982
+ }
983
+ }
984
+ });
985
+ const shouldAddAdditionalLocales = yield select({
986
+ message: "Would you like to add additional languages?",
987
+ choices: [
988
+ { name: "Yes", value: true },
989
+ { name: "No", value: false }
990
+ ]
991
+ });
992
+ let additionalLocales = [];
993
+ if (shouldAddAdditionalLocales) {
994
+ additionalLocales = yield multiSelect({
995
+ choices: availableLocales.filter(({ value }) => value !== storefrontLocale),
996
+ message: "Which additional languages would you like to add to your channel?",
997
+ theme: {
998
+ style: {
999
+ help: () => chalk4.dim("(Select locale from the list or start typing the name)")
1000
+ }
1001
+ },
1002
+ validate: (selections) => {
1003
+ if (selections.length > 4) {
1004
+ return "You can only select up to 4 additional languages";
1005
+ }
1006
+ return true;
1007
+ }
1008
+ });
1009
+ }
687
1010
  const shouldInstallSampleData = yield select({
688
1011
  message: "Would you like to install sample data?",
689
1012
  choices: [
@@ -691,10 +1014,15 @@ function handleChannelCreation(cliApi) {
691
1014
  { name: "No", value: false }
692
1015
  ]
693
1016
  });
694
- const response = yield cliApi.createChannel(newChannelName, shouldInstallSampleData);
1017
+ const response = yield cliApi.createChannel(
1018
+ newChannelName,
1019
+ storefrontLocale,
1020
+ additionalLocales,
1021
+ shouldInstallSampleData
1022
+ );
695
1023
  if (!response.ok) {
696
1024
  console.error(
697
- chalk3.red(`
1025
+ chalk4.red(`
698
1026
  POST /channels/catalyst failed: ${response.status} ${response.statusText}
699
1027
  `)
700
1028
  );
@@ -702,7 +1030,7 @@ POST /channels/catalyst failed: ${response.status} ${response.statusText}
702
1030
  }
703
1031
  const channelData = yield response.json();
704
1032
  if (!isCreateChannelResponse(channelData)) {
705
- console.error(chalk3.red("\nUnexpected response format from create channel endpoint\n"));
1033
+ console.error(chalk4.red("\nUnexpected response format from create channel endpoint\n"));
706
1034
  process.exit(1);
707
1035
  }
708
1036
  return {
@@ -718,7 +1046,7 @@ function handleChannelSelection(bc) {
718
1046
  const channelsResponse = yield bc.fetch("/v3/channels?available=true&type=storefront");
719
1047
  if (!channelsResponse.ok) {
720
1048
  console.error(
721
- chalk3.red(
1049
+ chalk4.red(
722
1050
  `
723
1051
  GET /v3/channels failed: ${channelsResponse.status} ${channelsResponse.statusText}
724
1052
  `
@@ -728,7 +1056,7 @@ GET /v3/channels failed: ${channelsResponse.status} ${channelsResponse.statusTex
728
1056
  }
729
1057
  const availableChannels = yield channelsResponse.json();
730
1058
  if (!isChannelsResponse(availableChannels)) {
731
- console.error(chalk3.red("\nUnexpected response format from channels endpoint\n"));
1059
+ console.error(chalk4.red("\nUnexpected response format from channels endpoint\n"));
732
1060
  process.exit(1);
733
1061
  }
734
1062
  const existingChannel = yield select({
@@ -756,7 +1084,7 @@ function getChannelInit(cliApi, channelId) {
756
1084
  const initResponse = yield cliApi.getChannelInit(channelId);
757
1085
  if (!initResponse.ok) {
758
1086
  console.error(
759
- chalk3.red(
1087
+ chalk4.red(
760
1088
  `
761
1089
  GET /channels/${channelId}/init failed: ${initResponse.status} ${initResponse.statusText}
762
1090
  `
@@ -766,7 +1094,7 @@ GET /channels/${channelId}/init failed: ${initResponse.status} ${initResponse.st
766
1094
  }
767
1095
  const initData = yield initResponse.json();
768
1096
  if (!isInitResponse(initData)) {
769
- console.error(chalk3.red("\nUnexpected response format from init endpoint\n"));
1097
+ console.error(chalk4.red("\nUnexpected response format from init endpoint\n"));
770
1098
  process.exit(1);
771
1099
  }
772
1100
  return {
@@ -779,7 +1107,7 @@ function setupProject(options) {
779
1107
  return __async(this, null, function* () {
780
1108
  let { projectName, projectDir } = options;
781
1109
  if (!pathExistsSync(projectDir)) {
782
- console.error(chalk3.red(`Error: --projectDir ${projectDir} is not a valid path
1110
+ console.error(chalk4.red(`Error: --projectDir ${projectDir} is not a valid path
783
1111
  `));
784
1112
  process.exit(1);
785
1113
  }
@@ -787,7 +1115,7 @@ function setupProject(options) {
787
1115
  projectName = kebabCase(projectName);
788
1116
  projectDir = join3(options.projectDir, projectName);
789
1117
  if (pathExistsSync(projectDir)) {
790
- console.error(chalk3.red(`Error: ${projectDir} already exists
1118
+ console.error(chalk4.red(`Error: ${projectDir} already exists
791
1119
  `));
792
1120
  process.exit(1);
793
1121
  }
@@ -815,16 +1143,16 @@ function setupProject(options) {
815
1143
  }
816
1144
  function checkRequiredTools() {
817
1145
  try {
818
- execSync5(getPlatformCheckCommand("git"), { stdio: "ignore" });
1146
+ execSync3(getPlatformCheckCommand("git"), { stdio: "ignore" });
819
1147
  } catch (e) {
820
- console.error(chalk3.red("Error: git is required to create a Catalyst project\n"));
1148
+ console.error(chalk4.red("Error: git is required to create a Catalyst project\n"));
821
1149
  process.exit(1);
822
1150
  }
823
1151
  try {
824
- execSync5(getPlatformCheckCommand("pnpm"), { stdio: "ignore" });
1152
+ execSync3(getPlatformCheckCommand("pnpm"), { stdio: "ignore" });
825
1153
  } catch (e) {
826
- console.error(chalk3.red("Error: pnpm is required to create a Catalyst project\n"));
827
- console.error(chalk3.yellow("Tip: Enable it by running `corepack enable pnpm`\n"));
1154
+ console.error(chalk4.red("Error: pnpm is required to create a Catalyst project\n"));
1155
+ console.error(chalk4.yellow("Tip: Enable it by running `corepack enable pnpm`\n"));
828
1156
  process.exit(1);
829
1157
  }
830
1158
  }
@@ -882,15 +1210,15 @@ Creating '${projectName}' at '${projectDir}'
882
1210
  console.log(
883
1211
  [
884
1212
  `
885
- ${chalk3.green("Success!")} Created '${projectName}' at '${projectDir}'
1213
+ ${chalk4.green("Success!")} Created '${projectName}' at '${projectDir}'
886
1214
  `,
887
1215
  `Next steps:`,
888
- Object.keys(envVars).length > 0 ? chalk3.yellow(`
1216
+ Object.keys(envVars).length > 0 ? chalk4.yellow(`
889
1217
  - cd ${projectName} && pnpm run dev
890
1218
  `) : [
891
- chalk3.yellow(`
1219
+ chalk4.yellow(`
892
1220
  - cd ${projectName} && cp .env.example .env.local`),
893
- chalk3.yellow(`
1221
+ chalk4.yellow(`
894
1222
  - Populate .env.local with your BigCommerce API credentials
895
1223
  `)
896
1224
  ].join("")
@@ -917,7 +1245,7 @@ ${chalk3.green("Success!")} Created '${projectName}' at '${projectDir}'
917
1245
  const eligibilityResponse = yield cliApi.checkEligibility();
918
1246
  if (!eligibilityResponse.ok) {
919
1247
  console.error(
920
- chalk3.red(
1248
+ chalk4.red(
921
1249
  `
922
1250
  GET /channels/catalyst/eligibility failed: ${eligibilityResponse.status} ${eligibilityResponse.statusText}
923
1251
  `
@@ -927,11 +1255,11 @@ GET /channels/catalyst/eligibility failed: ${eligibilityResponse.status} ${eligi
927
1255
  }
928
1256
  const eligibilityData = yield eligibilityResponse.json();
929
1257
  if (!isEligibilityResponse(eligibilityData)) {
930
- console.error(chalk3.red("\nUnexpected response format from eligibility endpoint\n"));
1258
+ console.error(chalk4.red("\nUnexpected response format from eligibility endpoint\n"));
931
1259
  process.exit(1);
932
1260
  }
933
1261
  if (!eligibilityData.data.eligible) {
934
- console.warn(chalk3.yellow(eligibilityData.data.message));
1262
+ console.warn(chalk4.yellow(eligibilityData.data.message));
935
1263
  }
936
1264
  let shouldCreateChannel;
937
1265
  if (eligibilityData.data.eligible) {
@@ -944,13 +1272,13 @@ GET /channels/catalyst/eligibility failed: ${eligibilityResponse.status} ${eligi
944
1272
  });
945
1273
  }
946
1274
  if (shouldCreateChannel) {
947
- const channelData = yield handleChannelCreation(cliApi);
1275
+ const channelData = yield handleChannelCreation(bc, cliApi);
948
1276
  channelId = channelData.channelId;
949
1277
  storefrontToken = channelData.storefrontToken;
950
1278
  envVars = __spreadValues({}, channelData.envVars);
951
- console.log(chalk3.green(`Channel created successfully`));
1279
+ console.log(chalk4.green(`Channel created successfully`));
952
1280
  console.warn(
953
- chalk3.yellow(
1281
+ chalk4.yellow(
954
1282
  "\nNote: A preview storefront has been deployed in your BigCommerce control panel. This preview may look different from your local environment as it may be running different code."
955
1283
  )
956
1284
  );
@@ -996,10 +1324,10 @@ Creating '${projectName}' at '${projectDir}'
996
1324
  }
997
1325
  console.log(
998
1326
  `
999
- ${chalk3.green("Success!")} Created '${projectName}' at '${projectDir}'
1327
+ ${chalk4.green("Success!")} Created '${projectName}' at '${projectDir}'
1000
1328
  `,
1001
1329
  "\nNext steps:\n",
1002
- chalk3.yellow(`
1330
+ chalk4.yellow(`
1003
1331
  cd ${projectName} && pnpm run dev
1004
1332
  `)
1005
1333
  );
@@ -1022,7 +1350,7 @@ function isChannelsResponse(response) {
1022
1350
  // src/commands/init.ts
1023
1351
  import { Command as Command2, Option as Option2 } from "@commander-js/extra-typings";
1024
1352
  import { select as select2 } from "@inquirer/prompts";
1025
- import chalk4 from "chalk";
1353
+ import chalk5 from "chalk";
1026
1354
  function isChannelsResponse2(response) {
1027
1355
  return typeof response === "object" && response !== null && "data" in response && Array.isArray(response.data) && response.data.every(
1028
1356
  (item) => typeof item === "object" && item !== null && "id" in item && "name" in item && "platform" in item
@@ -1066,7 +1394,7 @@ var init = new Command2("init").description("Connect a BigCommerce store with an
1066
1394
  const channelsResponse = yield bc.fetch("/v3/channels?available=true&type=storefront");
1067
1395
  if (!channelsResponse.ok) {
1068
1396
  console.error(
1069
- chalk4.red(
1397
+ chalk5.red(
1070
1398
  `
1071
1399
  GET /v3/channels failed: ${channelsResponse.status} ${channelsResponse.statusText}
1072
1400
  `
@@ -1076,7 +1404,7 @@ GET /v3/channels failed: ${channelsResponse.status} ${channelsResponse.statusTex
1076
1404
  }
1077
1405
  const availableChannels = yield channelsResponse.json();
1078
1406
  if (!isChannelsResponse2(availableChannels)) {
1079
- console.error(chalk4.red("\nUnexpected response format from channels endpoint\n"));
1407
+ console.error(chalk5.red("\nUnexpected response format from channels endpoint\n"));
1080
1408
  process.exit(1);
1081
1409
  }
1082
1410
  const existingChannel = yield select2({
@@ -1093,7 +1421,7 @@ GET /v3/channels failed: ${channelsResponse.status} ${channelsResponse.statusTex
1093
1421
  const initResponse = yield cliApi.getChannelInit(channelId);
1094
1422
  if (!initResponse.ok) {
1095
1423
  console.error(
1096
- chalk4.red(
1424
+ chalk5.red(
1097
1425
  `
1098
1426
  GET /channels/${channelId}/init failed: ${initResponse.status} ${initResponse.statusText}
1099
1427
  `
@@ -1103,7 +1431,7 @@ GET /channels/${channelId}/init failed: ${initResponse.status} ${initResponse.st
1103
1431
  }
1104
1432
  const initData = yield initResponse.json();
1105
1433
  if (!isInitResponse2(initData)) {
1106
- console.error(chalk4.red("\nUnexpected response format from init endpoint\n"));
1434
+ console.error(chalk5.red("\nUnexpected response format from init endpoint\n"));
1107
1435
  process.exit(1);
1108
1436
  }
1109
1437
  const envVars = __spreadValues({}, initData.data.envVars);
@@ -1118,13 +1446,13 @@ GET /channels/${channelId}/init failed: ${initResponse.status} ${initResponse.st
1118
1446
  Object.assign(envVars, cliEnvVars);
1119
1447
  }
1120
1448
  writeEnv(projectDir, envVars);
1121
- console.log(chalk4.green(`
1449
+ console.log(chalk5.green(`
1122
1450
  .env.local file created for channel ${existingChannel.name}!
1123
1451
  `));
1124
- console.log(chalk4.green(`
1452
+ console.log(chalk5.green(`
1125
1453
  Next steps:
1126
1454
  `));
1127
- console.log(chalk4.yellow(`
1455
+ console.log(chalk5.yellow(`
1128
1456
  pnpm run dev
1129
1457
  `));
1130
1458
  }));
@@ -1137,16 +1465,16 @@ import { outputFileSync as outputFileSync2, writeJsonSync } from "fs-extra/esm";
1137
1465
  import kebabCase2 from "lodash.kebabcase";
1138
1466
  import { coerce, compare } from "semver";
1139
1467
  import { promisify } from "util";
1140
- import { z as z3 } from "zod";
1468
+ import { z as z4 } from "zod";
1141
1469
  var exec = promisify(execCb);
1142
- var ManifestSchema = z3.object({
1143
- name: z3.string(),
1144
- dependencies: z3.object({ add: z3.array(z3.string()) }),
1145
- devDependencies: z3.object({ add: z3.array(z3.string()) }),
1146
- environmentVariables: z3.array(z3.string())
1470
+ var ManifestSchema = z4.object({
1471
+ name: z4.string(),
1472
+ dependencies: z4.object({ add: z4.array(z4.string()) }),
1473
+ devDependencies: z4.object({ add: z4.array(z4.string()) }),
1474
+ environmentVariables: z4.array(z4.string())
1147
1475
  });
1148
1476
  var integration = new Command3("integration").argument("<integration-name>", "Formatted name of the integration").option("--commit-hash <hash>", "Override integration source branch with a specific commit hash").action((integrationNameRaw, options) => __async(void 0, null, function* () {
1149
- const integrationName = z3.string().transform(kebabCase2).parse(integrationNameRaw);
1477
+ const integrationName = z4.string().transform(kebabCase2).parse(integrationNameRaw);
1150
1478
  const manifest = {
1151
1479
  name: integrationName,
1152
1480
  dependencies: { add: [] },
@@ -1168,9 +1496,9 @@ var integration = new Command3("integration").argument("<integration-name>", "Fo
1168
1496
  }
1169
1497
  return 0;
1170
1498
  }).reverse();
1171
- const PackageDependenciesSchema = z3.object({
1172
- dependencies: z3.object({}).passthrough(),
1173
- devDependencies: z3.object({}).passthrough()
1499
+ const PackageDependenciesSchema = z4.object({
1500
+ dependencies: z4.object({}).passthrough(),
1501
+ devDependencies: z4.object({}).passthrough()
1174
1502
  });
1175
1503
  const getPackageDeps = (ref) => __async(void 0, null, function* () {
1176
1504
  const { stdout } = yield exec(`git show ${ref}:core/package.json`);
@@ -1206,7 +1534,7 @@ var integration = new Command3("integration").argument("<integration-name>", "Fo
1206
1534
  import { Argument, Command as Command4, Option as Option3 } from "@commander-js/extra-typings";
1207
1535
 
1208
1536
  // src/utils/telemetry/index.ts
1209
- import chalk5 from "chalk";
1537
+ import chalk6 from "chalk";
1210
1538
  var telemetry3 = new Telemetry();
1211
1539
  var isEnabled = telemetry3.isEnabled();
1212
1540
  var catalystTelemetry = (options, arg) => {
@@ -1227,7 +1555,7 @@ var catalystTelemetry = (options, arg) => {
1227
1555
  }
1228
1556
  console.log(
1229
1557
  `
1230
- Status: ${chalk5.bold(isEnabled ? chalk5.green("Enabled") : chalk5.red("Disabled"))}`
1558
+ Status: ${chalk6.bold(isEnabled ? chalk6.green("Enabled") : chalk6.red("Disabled"))}`
1231
1559
  );
1232
1560
  if (!isEnabled) {
1233
1561
  console.log(
@@ -1280,7 +1608,7 @@ var telemetryPostHook = () => __async(void 0, null, function* () {
1280
1608
  });
1281
1609
 
1282
1610
  // src/index.ts
1283
- console.log(chalk6.cyanBright(`
1611
+ console.log(chalk7.cyanBright(`
1284
1612
  \u25E2 ${package_default.name} v${package_default.version}
1285
1613
  `));
1286
1614
  program.name(package_default.name).version(package_default.version).description("A command line tool to create a new Catalyst project.").addCommand(create, { isDefault: true }).addCommand(init).addCommand(integration).addCommand(telemetry4).hook("preAction", telemetryPreHook).hook("postAction", telemetryPostHook);