Skip to content

TRAC-137: Add native hosting option in catalyst CLI#3003

Draft
jordanarldt wants to merge 1 commit intoalphafrom
feat/trac-137-catalyst-cli
Draft

TRAC-137: Add native hosting option in catalyst CLI#3003
jordanarldt wants to merge 1 commit intoalphafrom
feat/trac-137-catalyst-cli

Conversation

@jordanarldt
Copy link
Copy Markdown
Contributor

@jordanarldt jordanarldt commented May 4, 2026

Jira: LTRAC-137

What/Why?

Following review of #2995, the team agreed that hosting/deploy concerns belong in catalyst (the CLI), not create-catalyst (the scaffolder). Since the broader plan is to absorb create-catalyst into catalyst anyway, this PR ports the work from #2995 directly into packages/catalyst as a new catalyst create command, plus a few related improvements that came out of building on that foundation.

catalyst create

New top-level command in packages/catalyst. Replaces the eventual role of create-catalyst create. Behavior summary:

  • Scaffold-only by default. No interactive hosting prompt — most users will reach Commerce Hosting via catalyst deploy, not at create time.
  • --hosting commerce opts into the full Commerce Hosting setup eagerly (proxy → middleware swap, OpenNext dep, .bigcommerce/project.json, core/.env.local symlink).
  • setupCoreProject runs unconditionally at create time, wiring catalyst build/start/deploy scripts and the @bigcommerce/catalyst dep into core/package.json regardless of hosting choice. This is what lets the polymorphic catalyst build/start commands (below) bridge the create→deploy gap for users who don't choose hosting upfront.
  • Reuses existing catalyst auth device flow — no parallel auth implementation.

Shared lib/commerce-hosting.ts

The Commerce Hosting setup logic from #2995 is extracted into a reusable module. Exports:

  • setupCommerceHosting({ projectDir, projectUuid, storeHash?, accessToken? }) — synchronous file mutations. Idempotent.
  • promptForCommerceHostingProject(...)catalyst create --hosting commerce semantics (default-name + auto-create on no collision).
  • selectOrCreateInfrastructureProject(api, linkedProjectUuid?) — generic "select existing or create new" flow used by catalyst project link and catalyst deploy. Decorates the matching project with [linked] when a UUID is passed, and skips the select prompt entirely when the store has zero projects.
  • NoLinkedProjectError — exported sentinel so callers can translate user decline into context-appropriate messaging.
  • runCommerceHostingSetup(...) — orchestrator (prompt → setup) for the create-time flow.

Polymorphic catalyst build / catalyst start

Both commands now dispatch via a new lib/project-state.ts helper. If the project isn't transformed for Commerce Hosting (middleware.ts + @opennextjs/cloudflare dep), they fall through to pnpm exec next build / next start. Otherwise they run the OpenNext pipeline. This is what lets setupCoreProject always wire build: "npm run generate && catalyst build" / start: "catalyst start" at create time, regardless of hosting choice.

catalyst project link / list

  • catalyst project list now marks the currently linked project with green [linked].
  • The select prompt in catalyst project link does the same.
  • After a successful link, offers to run Commerce Hosting setup if the project isn't already transformed.

catalyst deploy

  • Verifies the linked project still exists on the store before doing any build/upload work. If projectUuid is missing or 404s, falls through to selectOrCreateInfrastructureProject. Graceful info+exit when the user declines to create one.
  • Transformation guard: if the project isn't set up for Commerce Hosting, prompts to run setup before attempting OpenNext build. Declining cleanly exits.

buildWorkspacePackages

After installDependencies at create time, if the cloned repo is the catalyst monorepo (has core/, packages/catalyst, packages/client), runs pnpm build in the workspace packages so core can resolve them. No-op for non-monorepo scaffolds.

Misc / quality

  • No more .bigcommerce/ cwd pollution. Conf eagerly creates its config dir on construction; telemetry was always triggering this even from commands that didn't need project config. Telemetry moved to a user-scoped Conf (lib/user-config.ts, lives in OS config dir under catalyst-cli/). Project state checks now use a separate read-only getProjectState() helper that never instantiates Conf.
  • Canonical package.json field order (lib/sort-package-json.ts) applied whenever we mutate a package.json.
  • Shared option helpers in lib/shared-options.ts (already existed for logs) now used across project, create, and deploy — eliminates ~30 lines of duplicated --store-hash / --access-token / --api-host / --project-uuid option declarations. Each helper accepts an optional description override (deploy uses this to surface its .bigcommerce/project.json fallback hint).
  • Single --env parser (validates KEY=VALUE, preserves values containing =) and --channel-id parser (validates numeric) extracted as named functions.
  • BIGCOMMERCE_STOREFRONT_API_TOKEN typo fixed — the storefront actually reads BIGCOMMERCE_STOREFRONT_TOKEN (no _API_).

Testing

Unit tests: 228 total, all passing. New coverage:

  • commands/create.spec.ts (new, 12 tests) — happy paths (full flags, --hosting commerce, login-when-creds-missing), parser validation (--channel-id/--env regression tests), ordering invariants (writeEnv before install/build), failure handling (cleanup warning on partial state), --hosting commerce precondition (hasProjectsAccess returning false).
  • lib/project-state.spec.ts (new, 13 tests) — linked / transformed / fully-set-up detection across realistic project layouts.
  • lib/setup-core-project.spec.ts (new, 5 tests) — script + dep wiring at create time.
  • lib/sort-package-json.spec.ts (new, 5 tests) — canonical field ordering.
  • lib/commerce-hosting.spec.ts — covers prompts, setup, orchestrator, and the new selectOrCreateInfrastructureProject (empty-list, decline, [linked] marker paths).
  • commands/project.spec.ts — extended with empty-list, [linked]-marker, and graceful-decline assertions for link.
  • commands/deploy.spec.ts — extended with verify-or-prompt cases (linked-but-deleted, no-link), empty-list handling, transformation guard.

pnpm --filter @bigcommerce/catalyst test, typecheck, lint, and build all clean.

Manual verification:

  • catalyst create --project-name X (interactive, no hosting) — scaffolds, no .bigcommerce/project.json, no proxy → middleware swap. pnpm run build falls through to next build.
  • catalyst create --project-name X --hosting commerce — Commerce Hosting prompt fires before clone. Post-clone: scripts wired, core/.bigcommerce/project.json written, core/.env.local symlink created, core/proxy.ts → core/middleware.ts, @opennextjs/cloudflare installed.
  • catalyst project link[linked] shown for the matching project; offers Commerce Hosting setup after linking; declining exits cleanly with guidance.
  • catalyst project list[linked] marker on the currently linked project.
  • catalyst deploy from a fresh self-hosted scaffold — prompts for Commerce Hosting setup (transformation guard), then proceeds.
  • catalyst deploy against a linked-but-deleted project — falls through to select-or-create; declining exits cleanly.

Migration

packages/create-catalyst is not modified in this PR — the staged #2995 changes there were reverted. A follow-up PR will update create-catalyst create to shim into catalyst create so pnpm create @bigcommerce/catalyst keeps working without duplicated logic.

No env-var or flag breaking changes for end users of existing commands. The BIGCOMMERCE_STOREFRONT_API_TOKEN env var name (only ever written by the create flow, never read by the storefront) is now correctly BIGCOMMERCE_STOREFRONT_TOKEN.

🤖 Generated with Claude Code

@vercel
Copy link
Copy Markdown

vercel Bot commented May 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
catalyst Ready Ready Preview, Comment May 4, 2026 9:48pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 4, 2026

⚠️ No Changeset found

Latest commit: e13fec9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Bundle Size Report

Comparing against baseline from ec92930 (2026-05-04).

No bundle size changes detected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant