doc-site
Developer guide

Local development

Get the four-process dev stack running, and fix it when it does not start.

Local development

Prerequisites

RequirementNotes
Node.js 24.4.0Pinned in .nvmrc. Use nvm use
Shopify CLIInstalled globally and authenticated with shopify auth login
Store accessAccess to the a-sales-affair store

npm install also installs a cloudflared wrapper, which downloads the Cloudflare binary on its first run.

First run

An interactive wizard covers the whole setup:

npm run onboard

It checks prerequisites, prompts for authentication, installs dependencies, writes the store handle into shopify.theme.toml, offers to pull the theme, and optionally starts the dev server.

For an existing clone of this repository the store is already configured, so the manual path is shorter:

nvm use
npm install
npm run dev

What npm run dev starts

Four processes run under run-pty, defined in run-pty.json. Each keeps its own screen instead of interleaving output.

ProcessAddressRole
vitehttps://localhost:5173Module server with HTTPS and hot module replacement
cssnonePostCSS in watch mode, rebuilding the Tailwind output
shopifyhttp://127.0.0.1:9292shopify theme dev against the dev environment
tunnela trycloudflare.com URLHTTPS tunnel so the Shopify preview can load over HTTPS

In run-pty, ctrl+z opens the dashboard, 1 to 4 focus a process, enter restarts one that exited, and ctrl+c kills the focused process, or all of them from the dashboard.

Open the URL that cloudflared prints. Source entrypoints still load from the local Vite server, so hot module replacement keeps working in a browser on the development machine.

The first Vite run may ask for permission to install a locally trusted development certificate. That is vite-plugin-mkcert, and the HTTPS Vite server will not work without it.

Why there is a tunnel

Shopify's theme preview serves over HTTPS. A browser on an HTTPS page refuses to load modules from an HTTP origin, and Shopify's local proxy serves over HTTP on port 9292.

scripts/start-cloudflare-tunnel.mjs waits until port 9292 accepts connections, then starts cloudflared. It rewrites the origin Host header to 127.0.0.1:9292, because Shopify's local proxy expects that host and returns a JSON 400 when it receives the public trycloudflare.com hostname.

The dev environment

shopify.theme.toml defines two environments. Both point at the a-sales-affair store.

[environments.dev]
store = "a-sales-affair"
theme-editor-sync = true
development = true
error-overlay = "silent"
reconciliation-strategy = "keep-local"

development = true means shopify theme dev creates a development theme rather than serving from an existing one.

reconciliation-strategy = "keep-local" means that when local and remote files differ, your local copy wins. This protects your work in progress. It also means a theme editor change made while you are running the dev server is discarded locally.

theme-editor-sync = true pulls theme editor changes down into your working copy while the dev server runs. Expect templates/*.json and config/settings_data.json to change under you. Check git diff before committing, because a settings file can pick up an unrelated merchandiser edit.

Commands

CommandWhat it does
npm run devThe four-process stack
npm run dev:viteVite only
npm run dev:cssPostCSS watch only
npm run dev:shopifyShopify theme dev only
npm run dev:tunnelTunnel only
npm run buildPostCSS then Vite, rewriting assets/ and vite-tag.liquid
npm run typechecktsc --noEmit across src/
npm run lintShopify Theme Check
npm run lint:fixTheme Check with safe auto-corrections
npm run formatPrettier over everything, including Liquid
npm run format:checkPrettier in report-only mode
npm run consoleLiquid REPL against the dev environment
npm run profileLiquid render profiling for a page
npm run listList themes on the store with IDs and roles
npm run openOpen the dev theme preview

The full list is in npm scripts.

Debugging in the browser

Console output is suppressed by default. The theme replaces console.* with a conditional logger, so a stray console.log never reaches a customer.

Enable it with a query parameter, which persists to localStorage:

?ba_debug=*                 all output
?ba_debug=cart,mini-cart    only those namespaces

Or from the console:

window.enableLogging('*');
window.disableLogging();

Namespaces are listed in Debug logging.

Before you commit

npm run typecheck
npm run lint
npm run build

Check git status afterwards. A normal build touches several files in assets/ plus assets/vite-manifest.json and snippets/vite-tag.liquid. That is expected.

Confirm snippets/vite-tag.liquid points at hashed asset files rather than at localhost:5173. If it points at the dev server, the build did not run after you stopped the dev processes, and committing it breaks every bundle on the storefront.

When it will not start

Symptoms and fixes are in Dev server will not start.

On this page