Local development
Get the four-process dev stack running, and fix it when it does not start.
Local development
Prerequisites
| Requirement | Notes |
|---|---|
| Node.js 24.4.0 | Pinned in .nvmrc. Use nvm use |
| Shopify CLI | Installed globally and authenticated with shopify auth login |
| Store access | Access 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 onboardIt 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 devWhat npm run dev starts
Four processes run under run-pty, defined in run-pty.json. Each keeps its own screen instead of interleaving output.
| Process | Address | Role |
|---|---|---|
| vite | https://localhost:5173 | Module server with HTTPS and hot module replacement |
| css | none | PostCSS in watch mode, rebuilding the Tailwind output |
| shopify | http://127.0.0.1:9292 | shopify theme dev against the dev environment |
| tunnel | a trycloudflare.com URL | HTTPS 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
| Command | What it does |
|---|---|
npm run dev | The four-process stack |
npm run dev:vite | Vite only |
npm run dev:css | PostCSS watch only |
npm run dev:shopify | Shopify theme dev only |
npm run dev:tunnel | Tunnel only |
npm run build | PostCSS then Vite, rewriting assets/ and vite-tag.liquid |
npm run typecheck | tsc --noEmit across src/ |
npm run lint | Shopify Theme Check |
npm run lint:fix | Theme Check with safe auto-corrections |
npm run format | Prettier over everything, including Liquid |
npm run format:check | Prettier in report-only mode |
npm run console | Liquid REPL against the dev environment |
npm run profile | Liquid render profiling for a page |
npm run list | List themes on the store with IDs and roles |
npm run open | Open 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 namespacesOr from the console:
window.enableLogging('*');
window.disableLogging();Namespaces are listed in Debug logging.
Before you commit
npm run typecheck
npm run lint
npm run buildCheck 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.