Beauty Affairs HK

Build pipeline

How a TypeScript file in src/ becomes a script tag on the storefront.

Build pipeline: Tailwind CSS is compiled by PostCSS into theme.styles.css, which along with the rest of src/entrypoints is bundled by Vite into hashed files in assets plus a generated snippets/vite-tag.liquid lookup. Liquid templates render vite-tag to emit the matching script and stylesheet tags.

The two build steps

npm run build runs two commands in order.

npm run build:css runs PostCSS over src/assets/theme_in.css, which contains only the three Tailwind directives, and writes the result to src/entrypoints/theme.styles.css. Tailwind scans ./**/*.liquid and ./src/**/*.{js,ts,jsx,tsx} for class names, so a class used only in Liquid still ends up in the CSS.

vite build then bundles everything in src/entrypoints, including the theme.styles.css that PostCSS just wrote. It writes files with content hashes in their names to assets/, plus a manifest at assets/vite-manifest.json.

Running vite build on its own skips the Tailwind step, and the CSS will lack any utility class added since the last CSS build. Use npm run build.

Why the vite-tag snippet exists

Liquid cannot read the Vite manifest, so it cannot look up that mini-cart.ts compiled to mini-cart-B0CCwZRp.js. vite-plugin-shopify generates snippets/vite-tag.liquid to fill that gap. The snippet is a long chain of conditionals that maps each source path to its hashed asset and module preloads.

A Liquid file loads a bundle like this:

{%- render 'vite-tag' with 'mini-cart/elements/mini-cart.ts' -%}

The path is relative to src/entrypoints. If it matches no branch in the generated snippet, the render outputs nothing and no error appears anywhere. When a script is missing from the page, check this path first. See Asset not loading.

Generated files that are also committed

Git tracks two build outputs.

snippets/vite-tag.liquid is committed because the theme on Shopify needs it, and Shopify only receives files pushed to the theme.

src/entrypoints/theme.styles.css is committed because Vite reads it as input. Without it, a fresh clone cannot build the theme styles.

The next build overwrites a hand edit to either file. Change the sources instead. For styles, edit src/assets/theme_in.css and the Tailwind config. For scripts, edit the entrypoint.

What stays out of the theme

.shopifyignore keeps development files out of the theme upload. It excludes src/, scripts/, node_modules/, the Vite and Tailwind configs, package.json and the agent instruction files. Shopify receives the compiled output in assets/ plus the Liquid, config and locale files.

A developer who opens the theme in Shopify's code editor sees the compiled bundles and cannot see or edit the TypeScript.

Asset cleanup

vite.config.js adds a plugin called shopifyAssetCleanup. assets/ holds theme files alongside build output, and emptyOutDir is off. Without the plugin, old hashed files would pile up on every build and get pushed to Shopify.

At the start of a build, the plugin reads the previous manifest and deletes the files it lists. After the build, it removes any leftover file whose name matches the hashed pattern of a bundle the build still produces. It never touches files outside that pattern, so images and hand-written assets are safe.

Development mode

npm run dev starts four processes through run-pty:

PaneCommandPurpose
viteviteDev server on port 5173 over HTTPS, with hot module replacement
csspostcss --watchRebuilds theme.styles.css when Tailwind classes change
shopifyshopify theme devServes the theme on port 9292 against the dev environment
tunnelnode scripts/start-cloudflare-tunnel.mjsExposes the local preview on a public URL

In development, vite-tag points at the Vite dev server instead of hashed assets, so JavaScript and CSS changes appear without a theme push. The Shopify CLI syncs Liquid changes.

The Vite server uses HTTPS with a local certificate that vite-plugin-mkcert creates. On a fresh clone, the first run may ask for your password to install that certificate.

Starting the dev server rewrites snippets/vite-tag.liquid to point at localhost:5173 and the Vite client. The production theme depends on the same file, so run npm run build before you commit or push. After a dev session, check git status. If snippets/vite-tag.liquid is modified and you did not mean to rebuild, it holds dev server URLs that would break the storefront.

Source map

ConcernFile
Vite configuration, entrypoint discovery, asset cleanupvite.config.js
Tailwind content globs, brand colours, tw- prefixtailwind.config.cjs
PostCSS plugin chainpostcss.config.cjs
Build and dev scriptspackage.json
Dev process layoutrun-pty.json
TypeScript compiler options and @/ path aliastsconfig.json
Files excluded from the theme upload.shopifyignore

On this page