local-storefront 0.2.0 → 0.3.0-rc.1
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/CHANGELOG.md +1299 -0
- package/README.md +14 -9
- package/bin/lsf.js +4 -1
- package/docs/README.md +59 -0
- package/docs/admin-reference.md +1406 -0
- package/docs/admin.md +1751 -0
- package/docs/agents.md +366 -0
- package/docs/architecture.md +303 -0
- package/docs/checkout.md +1609 -0
- package/docs/cli.md +873 -0
- package/docs/compatibility.md +475 -0
- package/docs/control-api.md +251 -0
- package/docs/data-format.md +943 -0
- package/docs/data-reference.md +1247 -0
- package/docs/formatting.md +119 -0
- package/docs/performance.md +266 -0
- package/docs/quickstart-admin-api.md +211 -0
- package/docs/quickstart-app.md +271 -0
- package/docs/quickstart-functions.md +207 -0
- package/docs/quickstart-theme.md +177 -0
- package/docs/storefront.md +210 -0
- package/docs/strict-liquid.md +178 -0
- package/docs/testing.md +590 -0
- package/llms.txt +39 -0
- package/package.json +11 -8
package/docs/cli.md
ADDED
|
@@ -0,0 +1,873 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
Every command of local-storefront, its options, what it prints and how it exits.
|
|
4
|
+
`npx local-storefront <command> --help` prints the same options.
|
|
5
|
+
|
|
6
|
+
Installed with mise, from a release or from source, the command is `lsf`: `lsf serve` is
|
|
7
|
+
`npx local-storefront serve`.
|
|
8
|
+
|
|
9
|
+
## Options of every command
|
|
10
|
+
|
|
11
|
+
| Option | Environment variable | Default | What it does |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| `--theme <dir>` | `LSF_THEME` | `.` | The theme folder: the one with `layout/`, `sections/`, `templates/`. |
|
|
14
|
+
| `--data <dir>` | `LSF_DATA` | `<theme>/shopify-local` | The store data folder. Without one, the built-in demo store. |
|
|
15
|
+
| `--preview <name>` | `LSF_PREVIEW` | none | Turns on a Shopify feature preview. Repeat it, or separate names with commas. See [storefront](storefront.md#feature-previews). |
|
|
16
|
+
|
|
17
|
+
## Exit codes
|
|
18
|
+
|
|
19
|
+
| Code | Meaning |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `0` | Done, nothing to report. |
|
|
22
|
+
| `1` | Done, and something to fix: data or app errors, theme app extensions included (`validate`), problems in the theme (`check`), Liquid errors in the page or nothing at its path (`render --strict`), files not formatted (`fmt --check`), a Function that failed to run or did not run, a fixture whose output, target or export is not the run's (`app function run`), a scenario that fails or whose `expect` is not met, an extension the checkout does not offer, a stale module, an error in a theme app extension (`app check`), an extension that does not build or a theme app extension Shopify rejects (`app build`, `app function build`, `--build`), a webhook that was not delivered (`app webhook trigger`). |
|
|
23
|
+
| `2` | The command could not run: a wrong option, a theme or a file that cannot be read, a file `fmt` cannot write, a port `serve` cannot listen on. The message is on stderr, after `error:`. |
|
|
24
|
+
|
|
25
|
+
## Output
|
|
26
|
+
|
|
27
|
+
Results go to stdout; messages and summaries go to stderr. For a tool, `--format json` prints
|
|
28
|
+
one JSON document on stdout: `validate`, `check`, `app check`, `app function run` and
|
|
29
|
+
`app webhook trigger` take it. For a workflow of GitHub Actions, `--format github` prints
|
|
30
|
+
annotations: `check`, `validate` and `render` take it. The other commands print text and refuse
|
|
31
|
+
`--format`. What each one prints: [agents](agents.md#what-each-command-prints).
|
|
32
|
+
|
|
33
|
+
## serve
|
|
34
|
+
|
|
35
|
+
`npx local-storefront serve`
|
|
36
|
+
|
|
37
|
+
Serves the theme over HTTP. With `--app`, the checkout and the Admin API of the app too;
|
|
38
|
+
`--only` runs a part of them ([what runs](#what-runs)).
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx local-storefront serve --port 9292 --static --quiet --strict
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Option | Environment variable | What it does |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `--port <port>`, `-p` | | Listens on this port and no other: the server does not start if it is taken. `0` picks a free one. Without the option: `9292`, or the next free port up to `9391`, which a line says (`port: 9292 is taken, using 9293`). |
|
|
47
|
+
| `--host <address>` | | Address to listen on. Default `127.0.0.1`. `0.0.0.0` in a container. |
|
|
48
|
+
| `--static` | | Reads the theme and the data once. The fastest mode, for tests. |
|
|
49
|
+
| `--strict` | | Refuses to start when the store data has errors. |
|
|
50
|
+
| `--quiet`, `-q` | | Does not log requests. |
|
|
51
|
+
| `--live-reload` | | Reloads open pages when the theme or the data change. It adds a script to the pages: leave it off for tests. |
|
|
52
|
+
| `--customer <email>` | `LSF_CUSTOMER` | Visitors arrive logged in as this customer. Also `default` (the first customer) or `none`. |
|
|
53
|
+
| `--throttle <rules>` | `LSF_THROTTLE` | Answers late, to see loading states: `simulated`, `slow`, `300ms`, `cart-add=1s`. See [testing](testing.md#simulating-a-slow-network). |
|
|
54
|
+
| `--timings` | | Names every section and theme block in the `Server-Timing` header. See [performance](performance.md#render-times). |
|
|
55
|
+
| `--no-minify` | | Serves styles and scripts as written. Without it, they are minified with a source map, as on Shopify. |
|
|
56
|
+
| `--no-compression` | | Sends responses uncompressed. Without it, they are compressed (Brotli or gzip) for clients that accept it, as on Shopify. |
|
|
57
|
+
| `--app <dir>` | `LSF_APP` | A Shopify app. `/checkout` then runs its Functions and shows its checkout UI extensions, `/account` shows its customer account UI extensions, its backend gets the [Admin API](admin.md), and the theme renders the app blocks and app embed blocks of its [theme app extensions](storefront.md#theme-app-extensions). See [checkout](checkout.md). |
|
|
58
|
+
| `--only <parts>` | `LSF_ONLY` | Runs only these parts: `theme`, `checkout`, `admin`, comma-separated or repeated. Without it, the theme, and all three with `--app`. `checkout` and `admin` need `--app`. See [what runs](#what-runs). |
|
|
59
|
+
| `--extension <handle>` | | With `--app`: uses only this extension of the app. Repeat it for several. Without it, every extension is used. |
|
|
60
|
+
| `--build` | | With `--app`: builds the extensions first, as `app build` does: the Functions, then the UI extensions that name their sources. |
|
|
61
|
+
| `--no-functions` | | With `--app`: serves the app's UI extensions and its Admin API, but runs none of its Functions. For a backend's tests, whose orders a Function that fails would block: a discount of the data whose Function is not run is left out. A Function named with `--extension` is refused; not with `--watch`. |
|
|
62
|
+
| `--watch` | | With `--app`: builds a Function again when its files change, and reloads the open pages (it implies `--live-reload`). At start, builds only the Functions that have no module or changed since the last watch; with `--build`, all of them. Not with `--static`. See [watch mode](checkout.md#watch-mode). |
|
|
63
|
+
| `--variant <standard\|shop>` | | With `--app`: the look of the checkout page, `standard` by default. |
|
|
64
|
+
| `--app-url <url>` | `LSF_APP_URL` | Where the backend of the app listens (`http://localhost:3000`). The webhooks of `shopify.app.toml` go there (a relative `uri` is joined to it; an absolute one on the origin of `application_url` is moved to it), and so do the OAuth redirects and the launch of the embedded app. Without it, `application_url`. See [Admin API](admin.md). |
|
|
65
|
+
| `--client-secret <secret>` | `LSF_CLIENT_SECRET` | The secret webhooks, OAuth and session tokens are signed with. Default: `SHOPIFY_API_SECRET` of the environment, else of the app's `.env`, else `lsf-local-client-secret`, printed at start. |
|
|
66
|
+
| `--admin-throttle <plan>` | `LSF_ADMIN_THROTTLE` | The cost bucket of the Admin API, per shop and app: `store` (the default: Plus for store data with companies, Standard otherwise), `standard` (2000 points, 100 a second), `advanced` (4000, 200), `plus` (20000, 1000), `enterprise` (40000, 2000), `legacy` (1000, 50), or `off`. |
|
|
67
|
+
| `--admin-versions <list>` | `LSF_ADMIN_VERSIONS` | The versions of the Admin API served, comma-separated, among those lsf knows: `2026-10` alone by default, `2026-10,2027-01` with the release candidate. Another version answers `404`; one Shopify no longer supports is answered by the oldest stable version served, as Shopify does. See [API versions](admin.md#api-versions). |
|
|
68
|
+
| `--admin-fetch-local` | `LSF_ADMIN_FETCH_LOCAL` | Lets `themeCreate`, the `URL` bodies of `themeFilesUpsert` and `urlRedirectImportCreate` download their source from this machine or its network (the loopback, private, link-local and unique-local addresses), for a theme ZIP served from localhost on purpose. Off by default: Shopify cannot reach them, and a backend could otherwise read what the machine serves. See [Sources from a URL](admin-reference.md#themes-script-tags-redirects-in-bulk-and-policies). |
|
|
69
|
+
| `--admin-source <url>=<file>` | | Answers a URL with a file, never downloading it: what `themeCreate`, a `URL` body of `themeFilesUpsert` or `urlRedirectImportCreate` reads there. Repeatable. For your backend's own URLs (a theme ZIP on its CDN) that nothing serves in a test, wherever they point, `localhost` included. The file must exist when lsf starts, and is read at each use. `PUT /__lsf/admin/sources?url=` stubs others while lsf runs. See [Stubbed URLs](admin-reference.md#stubbed-urls). |
|
|
70
|
+
| `--pricing-plans <file>` | `LSF_PRICING_PLANS` | The plans of the app's [managed pricing](admin.md#managed-pricing) (Shopify App Pricing), which Shopify keeps in the Partner Dashboard: a TOML file of `[[plans]]` (JSON for a `.json` file). lsf's admin shows them at `/admin/charges/<app handle>/pricing_plans`, where the merchant chooses one, and the Billing API creates no charge, as on Shopify. A wrong file stops the start. Read at start, and again when it changes unless `--static` ([when the data changes on disk](#when-the-data-changes-on-disk); a wrong file then keeps the plans it had, and says why); `PUT /__lsf/admin/pricing-plans` changes them while lsf runs. Printed before `ready:` as `pricing: <n> plans (<file>): <page>`, then `partner: <url> (Partner API: …; token prtapi_lsf_…)`: where the app reads its plan ([Partner API](admin.md#read-the-plan-from-the-partner-api)). |
|
|
71
|
+
| `--polaris <online\|offline>` | `LSF_POLARIS` | What `/__lsf/cdn/<path>` answers, where an embedded app loads Polaris from: `online` (the default) redirects to Shopify's CDN, `offline` serves lsf's copy. See [Polaris without the internet](admin.md#polaris-without-the-internet). |
|
|
72
|
+
|
|
73
|
+
The options from `--extension` to `--variant` are the checkout's, those from `--app-url` to
|
|
74
|
+
`--polaris` the Admin API's. One given on the command line while its part does not run stops the
|
|
75
|
+
start: `error: --pricing-plans needs the admin part, which --only leaves out`, or without
|
|
76
|
+
`--app`, ``which needs the app: `--app DIR` (or LSF_APP)``. One set by its variable is ignored,
|
|
77
|
+
and said (`note: LSF_APP_URL (--app-url) is set, but the admin part is not served, …: ignored`):
|
|
78
|
+
a variable exported for `app dev` does not stop a `serve` of the theme alone.
|
|
79
|
+
|
|
80
|
+
Every line of the server goes to stderr; stdout stays empty. It prints the theme and the data
|
|
81
|
+
folder, the parts it runs (`serving: theme, checkout, admin`), then, once it takes requests, a
|
|
82
|
+
line that starts with `ready: http://127.0.0.1:<port>/`. With `--port 0`, read the port from
|
|
83
|
+
that line. Without `--quiet`, each request gets a line (`200 GET / 39.1ms`), with the reason of
|
|
84
|
+
a 404 above it.
|
|
85
|
+
|
|
86
|
+
With `--app`, the lines of the app come before `ready:`: how many checkout scenarios the data
|
|
87
|
+
has (when the checkout runs a Function), then, with the admin part, the [Admin API](admin.md)
|
|
88
|
+
of the store and its token, the client secret it signs with and where that secret comes from,
|
|
89
|
+
the webhooks of the app, its proxy, the embedded app in lsf's admin (`embedded:`), with
|
|
90
|
+
`--pricing-plans` the plans and the Partner API (`pricing:`, `partner:`), and with
|
|
91
|
+
`--polaris offline` where Polaris is served (`polaris:`). After `ready:`, with the theme and the
|
|
92
|
+
checkout, the address of the home page of the checkout, then `watch:` with `--watch`:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
serving: theme, checkout, admin
|
|
96
|
+
scenarios: 10
|
|
97
|
+
admin: http://127.0.0.1:9292/admin/api/2026-10/graphql.json
|
|
98
|
+
X-Shopify-Access-Token: shpat_… (shop: local-supply-co.myshopify.com)
|
|
99
|
+
client secret: from SHOPIFY_API_SECRET of ../my-app/.env
|
|
100
|
+
webhooks: 1 subscription of shopify.app.toml → http://localhost:3000
|
|
101
|
+
ready: http://127.0.0.1:9292/ (status and control API: http://127.0.0.1:9292/__lsf)
|
|
102
|
+
home: http://127.0.0.1:9292/__lsf/checkouts (extensions and checkout scenarios of the app)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Without `--app`, or with `--only` leaving the admin part out, it prints no `admin:`, secret,
|
|
106
|
+
`webhooks:`, `proxy:`, `embedded:`, `pricing:`, `partner:` or `polaris:` line. An app without an
|
|
107
|
+
`extensions` directory is a backend, and its line says so: `app: ../my-app (no extensions:
|
|
108
|
+
the Admin API, webhooks and the app proxy)`. The token is the same every run for a shop and an app; a tool reads it from
|
|
109
|
+
`GET /__lsf/admin` (`shops[0].token`). When the data changes on disk, the server reads it
|
|
110
|
+
again, and seeds the shops of the Admin API again only when the store's own data changed, not
|
|
111
|
+
for a checkout scenario ([when the data changes on disk](#when-the-data-changes-on-disk)).
|
|
112
|
+
|
|
113
|
+
While it runs, a webhook the backend refuses (`401`, `403`) gets one line, at its first refusal,
|
|
114
|
+
`--quiet` or not, naming the secret lsf signed it with as `client secret:` does:
|
|
115
|
+
`webhooks: products/create → http://localhost:3000/webhooks answered 401: lsf signed it with the
|
|
116
|
+
client secret from SHOPIFY_API_SECRET of ../my-app/.env: does the backend verify with the same
|
|
117
|
+
one?`.
|
|
118
|
+
|
|
119
|
+
It runs until it is stopped (Ctrl-C, `SIGTERM`, `SIGHUP`): then it finishes the requests it is
|
|
120
|
+
answering, for 3 seconds at most, abandons the rest, and exits with `0`. It exits with `2`
|
|
121
|
+
before `ready:` when it cannot listen on the port (`error: cannot listen on 127.0.0.1:9292:
|
|
122
|
+
Address already in use`), or, with `--strict`, when the store data has errors (each one, then
|
|
123
|
+
`error: the store data has errors (see above)`). A page whose Liquid never ends stops at its
|
|
124
|
+
render budget (see [the render budget](compatibility.md#the-render-budget)).
|
|
125
|
+
|
|
126
|
+
While it runs, tests talk to it through the [control API](control-api.md).
|
|
127
|
+
|
|
128
|
+
### What runs
|
|
129
|
+
|
|
130
|
+
lsf has three parts:
|
|
131
|
+
|
|
132
|
+
- **theme**: the storefront rendered from the theme: its pages, the cart, search, and the
|
|
133
|
+
stand-ins of the checkout (`/checkout` lists the cart) and of the customer accounts
|
|
134
|
+
(`/account`). With `--app`, the app blocks and app embed blocks of the app's
|
|
135
|
+
[theme app extensions](storefront.md#theme-app-extensions), and their assets at
|
|
136
|
+
`/cdn/extensions/<id>/assets/`.
|
|
137
|
+
- **checkout**: what `--app` brings to the shop: the [checkout](checkout.md), where its
|
|
138
|
+
Functions run, its UI extensions (checkout, thank-you page, order status, customer account at
|
|
139
|
+
`/account`), the scenarios and the home page of the app.
|
|
140
|
+
- **admin**: the [Admin API](admin.md), OAuth and tokens, webhook delivery, the app proxy,
|
|
141
|
+
lsf's admin (`/admin/…`, App Bridge, Polaris at `/__lsf/cdn/`), managed pricing and the
|
|
142
|
+
Partner API, and their control endpoints (`/__lsf/admin/…`, `/__lsf/webhooks`,
|
|
143
|
+
`/__lsf/polaris`).
|
|
144
|
+
|
|
145
|
+
`--only` (or `LSF_ONLY`) names the parts to run. Without it:
|
|
146
|
+
|
|
147
|
+
| Command | Runs |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `serve` | `theme` |
|
|
150
|
+
| `serve --app <dir>` | `theme`, `checkout`, `admin` |
|
|
151
|
+
| `app dev` | `checkout`, `admin`: `serve --only checkout,admin` |
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx local-storefront serve --only theme # the theme alone
|
|
155
|
+
npx local-storefront serve --app ../my-app --only admin # the Admin API alone, for a backend's tests
|
|
156
|
+
npx local-storefront serve --app ../my-app --only checkout # the extensions and the Functions alone
|
|
157
|
+
npx local-storefront serve --app ../my-app --only checkout,admin # what app dev serves
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`checkout` and `admin` need `--app`: without it, ``error: `--only admin` needs the app: `--app
|
|
161
|
+
DIR` (or LSF_APP)``. `app dev` serves no theme, so `app dev --only theme` is an error. Without
|
|
162
|
+
the theme, `serve` needs no theme folder, as `app dev`: the store data is still looked for in
|
|
163
|
+
`<theme>/shopify-local`, then in the app's.
|
|
164
|
+
|
|
165
|
+
A part that does not run answers `404` on its routes, with an `X-LSF-Reason` that names the
|
|
166
|
+
`--only` that serves it: ``the admin part is not served (serving: theme): `serve --app <dir>
|
|
167
|
+
--only theme,admin` serves it`` ([control API](control-api.md)). With the theme and without the
|
|
168
|
+
checkout, `/checkout` and `/account` are the theme's stand-ins, as without `--app`. Without the
|
|
169
|
+
theme, the pages of the theme answer that `404`. `/` is then the checkout's home page, and its
|
|
170
|
+
`404` names the checkout part when the checkout does not run either. `GET /__lsf/status`, `GET /__lsf/admin` and the page of `/__lsf` say which parts run.
|
|
171
|
+
|
|
172
|
+
The checkout without the admin part reads the owners of its Functions from the store data, and
|
|
173
|
+
records no order in an Admin API. The theme with the admin part shows what the backend writes,
|
|
174
|
+
with or without the checkout.
|
|
175
|
+
|
|
176
|
+
### When the data changes on disk
|
|
177
|
+
|
|
178
|
+
Without `--static`, the server looks at the data folder before it answers (every 300 ms at most)
|
|
179
|
+
and reads it again when a file changed. What it does then depends on the files, and the
|
|
180
|
+
terminal says which changed and what was done:
|
|
181
|
+
|
|
182
|
+
| What changed | What the server does | The line |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| The store's own data: a data file at the root (`store.json`), a file of `products/`, `collections/`, `pages/`, `blogs/`, `customers/`, `menus/`, or of `files/` | Reads everything again and seeds the shops of the Admin API again from it: what the backend changed there (a product, a subscription, a customization) is dropped. | `store data reloaded (products/tee.json changed): the shops of the Admin API were seeded again, and what the Admin API changed in local-supply-co.myshopify.com was dropped` |
|
|
185
|
+
| Only checkout scenarios (`checkouts/*.json`) | Reads the scenarios again on top of the same store data. The shops of the Admin API keep their state: the app's subscription, its customizations, its other writes. An open checkout follows its scenario. | `checkout scenarios reloaded (checkouts/b2b.json added): the store and what the Admin API changed are kept (no re-seed)` |
|
|
186
|
+
| The `--pricing-plans` file, wherever it is (looked at before every request, the control endpoints and lsf's admin included) | Reads the plans again. Plans a `PUT /__lsf/admin/pricing-plans` set stay until its `DELETE`. A file that no longer reads keeps the plans it had. | `pricing plans reloaded (pricing.toml)`, or `pricing plans not reloaded: …` |
|
|
187
|
+
| A file the store does not read (`schema/`, `README.md`, another folder, a name that starts with a dot) | Nothing. | none |
|
|
188
|
+
|
|
189
|
+
A scenario is a checkout input, not store data: adding one while the backend has set the shop
|
|
190
|
+
up must not undo that setup. A change that touches both kinds seeds again. `POST /__lsf/reload`
|
|
191
|
+
reads everything again and seeds the shops again whatever changed, and `DELETE /__lsf/admin`
|
|
192
|
+
puts them back to the store data without reading the files.
|
|
193
|
+
|
|
194
|
+
## app
|
|
195
|
+
|
|
196
|
+
The commands of a Shopify app, named as Shopify's CLI names them (`shopify app dev`,
|
|
197
|
+
`shopify app build`, `shopify app function run`). In the app's folder, `--app` can be left out,
|
|
198
|
+
and the store data defaults to the app's `shopify-local/`. Guide: [checkout](checkout.md).
|
|
199
|
+
|
|
200
|
+
| Command | What it does |
|
|
201
|
+
|---|---|
|
|
202
|
+
| [`app dev`](#app-dev) | Serves the app alone: its checkout, its customer account and its Admin API. |
|
|
203
|
+
| [`app init`](#app-init) | Writes checkout scenarios made for the app. |
|
|
204
|
+
| [`app build`](#app-build) | Builds its extensions, without Node. |
|
|
205
|
+
| [`app check`](#app-check) | Runs the Functions of every scenario and checks their `expect`, for CI. |
|
|
206
|
+
| [`app function run`](#app-function-run), [`app function build`](#app-function-build), [`app function schema`](#app-function-schema) | Runs or builds its Functions, without a server, or writes the schema of a Function's API. |
|
|
207
|
+
| [`app webhook trigger`](#app-webhook-trigger) | Sends one of its webhooks to its backend, signed as Shopify signs it. |
|
|
208
|
+
|
|
209
|
+
### app dev
|
|
210
|
+
|
|
211
|
+
`npx local-storefront app dev --app <dir>`
|
|
212
|
+
|
|
213
|
+
Serves a Shopify app alone, without a theme: `serve --app --only checkout,admin`. `/` is the
|
|
214
|
+
[home page](checkout.md#the-home-page), which lists the extensions of the app and the checkout
|
|
215
|
+
scenarios and opens a checkout for each; `/checkout` starts a checkout, where the Functions run
|
|
216
|
+
and the checkout UI extensions show; `/account` is the
|
|
217
|
+
[customer account](checkout.md#customer-account), where the customer account UI extensions
|
|
218
|
+
show. The Admin API, the control API and `/cdn` are there, other pages answer `404`. It takes
|
|
219
|
+
the options of [`serve`](#serve); `--only checkout` or `--only admin` runs one of its two parts
|
|
220
|
+
([what runs](#what-runs)), and `--only theme` is an error.
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx local-storefront app dev --app ../my-app --extension volume-discount --variant shop
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### app init
|
|
227
|
+
|
|
228
|
+
`npx local-storefront app init [--app <dir>]`
|
|
229
|
+
|
|
230
|
+
Writes checkout scenarios, the files of `checkouts/` in the store data, made for the Functions
|
|
231
|
+
and checkout UI extensions of the app (a generic set outside an app, or when nothing matches).
|
|
232
|
+
It gives each Function the settings its input query reads (`[extensions.input.variables]`) when
|
|
233
|
+
the store data has none: those of the app's own `shopify-local/`, or empty values of the type of
|
|
234
|
+
each variable, in `app-settings.json` (to fill in). It gives each UI extension that declares
|
|
235
|
+
settings (`[[extensions.settings.fields]]`) and has none in the store data a value of each
|
|
236
|
+
field's type there too: its first choice, `false`, `0` or `""`. It creates the data folder with the demo store when it does not exist, refreshes `schema/`, never
|
|
237
|
+
overwrites a scenario or a data file without `--force`, and prints what it wrote, what it kept
|
|
238
|
+
and what it skipped and why, then what to run next (`next: lsf app dev`, with `--app` when
|
|
239
|
+
the app is not the one of the current folder). It writes to the same data folder as `init`, `--data` and
|
|
240
|
+
`LSF_DATA` included. Guide: [checkout](checkout.md#scenarios).
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npx local-storefront app init --app ../my-app
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
| Option | Environment variable | What it does |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `--app <dir>` | `LSF_APP` | The app the scenarios are made for. Defaults to the app the current folder is in. |
|
|
249
|
+
| `--force` | | Overwrites the scenarios that exist. |
|
|
250
|
+
|
|
251
|
+
### app build
|
|
252
|
+
|
|
253
|
+
`npx local-storefront app build [handle]... --app <dir>`
|
|
254
|
+
|
|
255
|
+
Builds the extensions of an app, as `shopify app build` does, without Node: its Functions (as
|
|
256
|
+
`app function build`) and its UI extensions, of the checkout and of the customer account, that
|
|
257
|
+
name their sources (`module`), bundled into `dist/<handle>.js`. Guide:
|
|
258
|
+
[checkout](checkout.md#what-lsf-builds-without-node).
|
|
259
|
+
|
|
260
|
+
A theme app extension has nothing to build: it is checked as Shopify checks it when the app is
|
|
261
|
+
deployed. Its folders (`assets/`, `blocks/`, `snippets/`, `locales/`, no other), its limits (10
|
|
262
|
+
MB of files, 30 blocks, 100 KB of Liquid, 100 locale files of 15 KB at most; a warning past 100
|
|
263
|
+
KB of CSS or 10 KB of JavaScript, gzipped, named by a schema), the schema of each block
|
|
264
|
+
(`name`, `target`, settings with a `type` and a unique `id`, the files of `javascript` and
|
|
265
|
+
`stylesheet`, `available_if`, not both `enabled_on` and `disabled_on`), and its Liquid, with
|
|
266
|
+
Shopify's strict parser. Each one says `checked: <handle>: ...`; an error exits with `1`.
|
|
267
|
+
`validate` and `app check` run the same checks.
|
|
268
|
+
|
|
269
|
+
| Option | Environment variable | What it does |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| `[handle]...` | | The extensions to build or check. Every extension of the app when none is given. |
|
|
272
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in. |
|
|
273
|
+
|
|
274
|
+
### app check
|
|
275
|
+
|
|
276
|
+
`npx local-storefront app check [--app <dir>]`
|
|
277
|
+
|
|
278
|
+
Runs the Functions of every checkout scenario, as the home page checks them, without a server:
|
|
279
|
+
for CI. Prints each scenario that does not validate, each Function that fails, with the
|
|
280
|
+
scenarios it fails in, and each expectation of a scenario's [`expect`](checkout.md#expected-results)
|
|
281
|
+
that its checkout does not meet. Exits with `1` when there is one, when the time ran out, or
|
|
282
|
+
when there is no scenario at all (`app init` writes a set). No request of a `.fetch` target is
|
|
283
|
+
sent.
|
|
284
|
+
It builds nothing without `--build`: in a fresh clone, where `dist/` is not committed, give
|
|
285
|
+
`--build` (or run `npx local-storefront app build` first).
|
|
286
|
+
|
|
287
|
+
It also fails, with why, on:
|
|
288
|
+
|
|
289
|
+
- an extension the checkout does not offer: an `api_version` lsf does not run (it runs
|
|
290
|
+
`2026-10` and `2027-01`), a Function with no module, a checkout UI extension with no bundle,
|
|
291
|
+
and, for one `--extension`, a scenario's `extensions` or a discount names, targets the local
|
|
292
|
+
checkout does not have. A UI extension with no checkout or customer account target (an admin
|
|
293
|
+
one) is left out unless named;
|
|
294
|
+
- a Function whose files changed after its module was built: its module runs the old code. Give
|
|
295
|
+
`--build`, or build it (`app build`). lsf records what each build it makes was built from;
|
|
296
|
+
a module another tool built is stale when a file is newer than it by more than 2 seconds;
|
|
297
|
+
- what Shopify rejects in a theme app extension, as `app build` checks it. `--extension` naming
|
|
298
|
+
other extensions only leaves them out.
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
fails ui-preact: api_version "2026-07" is not supported (supported: 2026-10, 2027-01): the extension is not offered to the checkout page
|
|
302
|
+
stale discount: extensions/discount/src/run.ts changed after its module was built, which runs the old code: run with --build (or `lsf app build discount`)
|
|
303
|
+
invalid badges: extensions/badges/blocks/stars.liquid: a block needs a {% schema %}
|
|
304
|
+
checked 11 of 11 scenarios in 0.1 s: 0 failures, 0 invalid scenarios, 1 extension not offered, 1 stale module, 1 error in theme app extensions
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
A trap ends with the last message the Function logged, such as the one of a `throw`:
|
|
308
|
+
|
|
309
|
+
```text
|
|
310
|
+
fails my-discount · cart.lines.discounts.generate.run: the Function trapped: Error: boom (in b2b)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
A Function that ran in no scenario, such as a discount Function no scenario's discount names, is
|
|
314
|
+
a warning: nothing checked it, and the exit code stays the same. The last line, the summary,
|
|
315
|
+
counts it too, rather than saying that every Function ran:
|
|
316
|
+
|
|
317
|
+
```text
|
|
318
|
+
warning volume-discount: ran in no scenario, so nothing checks it (a discount Function runs only for a discount that names it: …)
|
|
319
|
+
checked 8 of 8 scenarios in 0.4 s: no Function failed, 1 Function ran in no scenario
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
A payment or delivery customization, validation or cart transform Function gets
|
|
323
|
+
``(it runs where the shop has an enabled owner of it, as on Shopify, or where a scenario names
|
|
324
|
+
it: add it to the `extensions` of one)`` instead.
|
|
325
|
+
|
|
326
|
+
An expectation that is not met prints the scenario and the assertion (the list and the rule,
|
|
327
|
+
or the amount), what the scenario expects, and what the checkout has, as the scenario writes it.
|
|
328
|
+
The Functions run for it as the order is placed, unless the `expect` says
|
|
329
|
+
`"step": "interaction"`, which the first line then names:
|
|
330
|
+
|
|
331
|
+
```text
|
|
332
|
+
expect express-buttons: express_methods.excludes
|
|
333
|
+
expected: no entry matching "paypal"
|
|
334
|
+
actual: [{"handle": "paypal", "name": "PayPal"}, {"handle": "shop_pay", "name": "Shop Pay"}]
|
|
335
|
+
expect express-buttons: totals.total
|
|
336
|
+
expected: "96.00"
|
|
337
|
+
actual: "86.40"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
When every Function ran somewhere and none failed, the summary ends with `every Function ran`,
|
|
341
|
+
then `12 expectations met` when scenarios have an `expect`. When something failed, it counts the
|
|
342
|
+
failures, the invalid scenarios and the expectations not met:
|
|
343
|
+
`1 failure, 0 invalid scenarios, 2 of 12 expectations unmet`.
|
|
344
|
+
|
|
345
|
+
Exiting with `0` means no Function failed where it ran, and every `expect` holds. A scenario
|
|
346
|
+
without `expect` checks only that its Functions run. CI: [checkout](checkout.md#in-ci).
|
|
347
|
+
|
|
348
|
+
`--extension` keeps some extensions of the app. An `expect` describes the checkout of the
|
|
349
|
+
Functions its scenario names (every Function of the app when it names none): when
|
|
350
|
+
`--extension` leaves one of them out, that checkout is another one, so the `expect` is not
|
|
351
|
+
checked. The scenario still runs, for the Functions that fail in it, and the summary counts
|
|
352
|
+
it:
|
|
353
|
+
|
|
354
|
+
```text
|
|
355
|
+
checked 8 of 8 scenarios in 0.1 s: every Function ran, the `expect` of 2 scenarios not checked (--extension leaves out a Function they run)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
| Option | Environment variable | What it does |
|
|
359
|
+
|---|---|---|
|
|
360
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in. |
|
|
361
|
+
| `--extension <handle>` | | Only these extensions. Repeat it for several. |
|
|
362
|
+
| `--build` | | Builds the extensions first, as `app build` does. |
|
|
363
|
+
| `--format text` | | Default. One line per problem, then a summary. |
|
|
364
|
+
| `--format json` | | `{ok, state, checked, total, elapsedMs, failures: [{handle, target, error, logs, scenarios}], invalid: [{scenario, file, problems}], expectations: {checked, unchecked: [scenario], unmet: [{scenario, file, step, assertion, expected, actual}]}, ranInNoScenario: [handle], notOffered: [{handle, reason}], stale: [{handle, file}], themeExtensions: [{handle, severity, message}]}`, and `error` when there is no scenario. `state` is `done`, or `partial` when the time ran out. A Function target that fails in several ways has a failure for each error, with the scenarios of that error; `logs` is what the Function logged in the first of them. `expectations.checked` counts the expectations of the scenarios' `expect`, met or not; `unchecked` names the scenarios whose `expect` was not checked, as `--extension` leaves out a Function they run; `assertion` is the list and the rule (`express_methods.excludes`) or the amount (`totals.total`), `expected` what the scenario says (the entry, the list, the amount), `actual` what the checkout has. `ranInNoScenario` is filled only when every scenario ran. `notOffered` has the extensions the checkout does not offer, `stale` the Functions whose `file` changed after their module was built, `themeExtensions` what Shopify rejects (`error`) or advises against (`warning`) in the theme app extensions. |
|
|
365
|
+
| `--budget <seconds>` | | How long the Functions may run in all. Default 300. |
|
|
366
|
+
|
|
367
|
+
### app function run
|
|
368
|
+
|
|
369
|
+
`npx local-storefront app function run [handle]... --app <dir>`
|
|
370
|
+
|
|
371
|
+
Runs the Functions of an app once, without a server. Builds the input of each from the store
|
|
372
|
+
data, runs them in checkout order, and prints each input, output and log with the checkout that
|
|
373
|
+
results. Guide: [checkout](checkout.md#run-a-function-without-a-server).
|
|
374
|
+
|
|
375
|
+
A Function whose files changed after its module was built is built first, as `shopify app
|
|
376
|
+
function run` builds before it runs: `build: <handle>: <file> changed after its module was
|
|
377
|
+
built: building it first`. One lsf cannot build (no build command) runs its old module, with a
|
|
378
|
+
warning. Before the buyer gives a shipping address, no delivery rate is selected, as on Shopify:
|
|
379
|
+
the shipping of the totals is `0.00`, and a line says so.
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
npx local-storefront app function run volume-discount --app ../my-app --session '{"cart":{"items":[{"variant":"TEE-BLK-M","quantity":3}]}}'
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
| Option | Environment variable | What it does |
|
|
386
|
+
|---|---|---|
|
|
387
|
+
| `[handle]...` | | The Functions to run, whether or not the shop has an owner of them (a payment customization, …): one the checkout of the store data would leave out is said first (`Named: …`, `forced` in JSON). It reads the store data on disk, not a running lsf. Without them, what the checkout runs: the Functions with an enabled owner, those of a discount, or those the scenario names; the others are listed as not run, with why. |
|
|
388
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in; the store data to its `shopify-local/` when the current folder has none. |
|
|
389
|
+
| `--build` | | Builds the extensions first, as `app build` does: the Functions, then the UI extensions that name their sources. |
|
|
390
|
+
| `--scenario <name>` | | A [checkout scenario](data-format.md#checkout-scenarios) of the store data (`checkouts/<name>.json`): its cart, buyer, language, `checkout` settings and the extensions it names. `--session`, `--checkout` and `--locale` go on top of it. |
|
|
391
|
+
| `--session <file or JSON>` | | The session to run for, as `PUT /__lsf/session` takes it: its `data` goes on top of the store data for this run (a discount for a discount Function, settings in `checkout.functions`). Defaults to the `session` of the store data. |
|
|
392
|
+
| `--checkout <file or JSON>` | | What the buyer entered: `email`, `phone`, `shippingAddress`, `selectedDelivery`, `selectedPayment`, `discountCodes`, `metafields`. |
|
|
393
|
+
| `--locale <iso>` | | The language of the buyer, one of the store's (`fr`, `pt-BR`): Functions read it as `localization.language`. Defaults to the primary language. |
|
|
394
|
+
| `--step <step>` | | `interaction` or `completion` (the default): the step of the buyer journey, what validation Functions read as `buyerJourney.step`. |
|
|
395
|
+
| `--format text` | | Default. Each run, then the checkout. |
|
|
396
|
+
| `--format json` | | The checkout as the checkout page reads it, with every run in `runs`. With `--input`, the run: `{handle, target, export, input, output, logs, error, instructions}`, and `mismatches: [{path, expected, actual}]` when a fixture expects another output. With `--input <folder>`: `{ok, handle, passed, failed, fixtures: [{file, ok, run, error}]}`. |
|
|
397
|
+
| `--input <file>`, `-i` | | Runs one Function on this input, as `shopify app function run --input` does, instead of one built from the store data: a JSON file, `-` for stdin, or a folder, whose `*.json` fixtures each run, with a line each and a summary (`ran 4 fixtures of discount: 3 passed, 1 failed`). The Function is the one named, else the one whose folder holds the input or the current folder, else the app's only Function. A fixture of the unit tests of Shopify's templates (`{"payload": {"export", "target", "input", "output"}}`) runs its target: its `target` and `export` must be the Function's (and `--export`'s), and its `output` the run's; where it is not, each path that differs is printed, with what the fixture expects and what the run answered. The output is checked against the schema of the Function's API and the instruction limit, as in a checkout. |
|
|
398
|
+
| `--export <name>`, `-e` | | The target to run, by its Wasm export (`cart-lines-discounts-generate-run`), as Shopify's `--export`, or by its name (`cart.lines.discounts.generate.run`): for `--input` and `--save-input`, when the Function has several targets. |
|
|
399
|
+
| `--save-input <file>` | | Writes the input lsf built for the run of the Function named (and of `--export`) to this file, for `--input` or for a unit test's fixture. Its folders are created. |
|
|
400
|
+
|
|
401
|
+
Exits with `1` when a Function fails to run, when a fixture of `--input` is not met or names
|
|
402
|
+
a target or an export the Function does not have, or when a Function named on the command line did not
|
|
403
|
+
run: it says why (no discount names a discount Function, the cart is empty, the `extensions` of
|
|
404
|
+
the scenario leave it out, an error of the app…). A handle or a scenario that is close to an existing one is suggested. Errors a validation
|
|
405
|
+
Function reports are part of the checkout and do not change the exit code.
|
|
406
|
+
|
|
407
|
+
### app function build
|
|
408
|
+
|
|
409
|
+
`npx local-storefront app function build [handle]... --app <dir>`
|
|
410
|
+
|
|
411
|
+
Builds the Functions of an app, as `shopify app build` does. A Function written in JavaScript or
|
|
412
|
+
TypeScript without a build command is built by lsf, without Node: its types
|
|
413
|
+
(`generated/api.ts`), `dist/function.js`, then its Wasm module. The others run their
|
|
414
|
+
`[extensions.build] command`. Guide: [checkout](checkout.md#javascript-and-typescript-functions).
|
|
415
|
+
|
|
416
|
+
| Option | Environment variable | What it does |
|
|
417
|
+
|---|---|---|
|
|
418
|
+
| `[handle]...` | | The Functions to build. Every Function of the app when none is given. |
|
|
419
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in. |
|
|
420
|
+
|
|
421
|
+
### app function typegen
|
|
422
|
+
|
|
423
|
+
`npx local-storefront app function typegen [handle]... --app <dir>`
|
|
424
|
+
|
|
425
|
+
Writes the GraphQL types of a JavaScript or TypeScript Function, as `shopify app function
|
|
426
|
+
typegen` does (the `typegen` script of Shopify's templates): the `codegen` section of its
|
|
427
|
+
`package.json`, written as graphql-codegen writes it (`generated/api.ts`), from its
|
|
428
|
+
`schema.graphql` or lsf's schema of its API version, offline. `app function build` writes them
|
|
429
|
+
too. A Rust Function's types come from the `typegen` macro at its build: it writes nothing.
|
|
430
|
+
|
|
431
|
+
| Option | Environment variable | What it does |
|
|
432
|
+
|---|---|---|
|
|
433
|
+
| `[handle]...` | | The Functions. Without them, the one the current folder is in, else every JavaScript Function. |
|
|
434
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in. |
|
|
435
|
+
|
|
436
|
+
### app function schema
|
|
437
|
+
|
|
438
|
+
`npx local-storefront app function schema [handle] [--path <dir>] [--stdout]`
|
|
439
|
+
|
|
440
|
+
Writes the GraphQL schema of a Function's API, at the `api_version` of its
|
|
441
|
+
`shopify.extension.toml`, to its `schema.graphql`, as `shopify app function schema` does: the
|
|
442
|
+
schema a Rust Function's `#[typegen("schema.graphql")]` and a JavaScript Function's
|
|
443
|
+
graphql-codegen read. It comes from the schemas lsf carries (the checkout's Function APIs:
|
|
444
|
+
cart transform, discounts, delivery and payment customizations, validations, at 2026-10 and
|
|
445
|
+
2027-01), without the network or a Partner account. Guide:
|
|
446
|
+
[checkout](checkout.md#the-schema-of-a-function).
|
|
447
|
+
|
|
448
|
+
| Option | Environment variable | What it does |
|
|
449
|
+
|---|---|---|
|
|
450
|
+
| `[handle]` | | The Function. Without it, the Function whose folder `--path` names, or the one the current folder is in. |
|
|
451
|
+
| `--path <dir>` | | The folder of the Function, as Shopify's `--path`. |
|
|
452
|
+
| `--app <dir>` | `LSF_APP` | The app. Defaults to the app `--path` or the current folder is in. |
|
|
453
|
+
| `--stdout` | | Prints the schema instead of writing `schema.graphql`. |
|
|
454
|
+
|
|
455
|
+
### app webhook trigger
|
|
456
|
+
|
|
457
|
+
`npx local-storefront app webhook trigger --topic <topic> [--app <dir> --app-url <url> | --address <url>]`
|
|
458
|
+
|
|
459
|
+
Sends one webhook, as `shopify app webhook trigger` does, without Shopify: lsf builds the
|
|
460
|
+
payload of the topic (the REST representation Shopify sends) from a record of the store data, as
|
|
461
|
+
the [Admin API](admin.md) seeds it (`--data`, else the `shopify-local/` of the theme or of the
|
|
462
|
+
app, else the demo store): the first product for `products/update`, the first order for
|
|
463
|
+
`orders/create`, the shop for `app/uninstalled`. A topic whose type the data has no record of
|
|
464
|
+
(a refund, a draft order) is built from lsf's sample, modelled on Shopify's example payload. lsf
|
|
465
|
+
signs it with the client secret (`X-Shopify-Hmac-Sha256`), and posts it with Shopify's headers,
|
|
466
|
+
`X-Shopify-Shop-Domain` the shop of the data. It prints the request it sent and the answer of the
|
|
467
|
+
backend. To send the webhook of a given record, use `POST /__lsf/webhooks/trigger` with its
|
|
468
|
+
`id` while a server runs. In the demo store, the first order, `#1001`, is fulfilled already: to
|
|
469
|
+
test a fulfillment handler, [place an order](admin.md#place-an-order), or trigger
|
|
470
|
+
`{"topic": "orders/create", "id": "gid://shopify/Order/7888001690128"}` (`#1002`).
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
npx local-storefront app webhook trigger --topic products/update --app ../my-app --app-url http://localhost:3000
|
|
474
|
+
npx local-storefront app webhook trigger --topic orders/create --address http://localhost:3000/webhooks --client-secret "$SHOPIFY_API_SECRET"
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Without `--address`, it goes to every subscription of `shopify.app.toml` to the topic, as an
|
|
478
|
+
event of the shop would: each subscription's `uri` joined to the app URL, its `include_fields`
|
|
479
|
+
applied. Relative `uri`s join `--app-url` (else `application_url`), and an absolute one on the
|
|
480
|
+
origin of `application_url` moves to `--app-url`, since `application_url` is often the tunnel of
|
|
481
|
+
a `shopify app dev` that is not running. As Shopify's, a triggered webhook is not retried.
|
|
482
|
+
|
|
483
|
+
Webhooks stay on this machine. One goes to another host only when that host is the scheme,
|
|
484
|
+
host and port of `--app-url`, or the `--address` given: otherwise the command sends nothing,
|
|
485
|
+
prints `error: lsf sends webhooks to this machine or to --app-url only: <url> is neither; give
|
|
486
|
+
--app-url http://localhost:<port> (your backend) or --address <url>` and exits with `2`. A
|
|
487
|
+
server logs the same delivery `not_sent`, and says to start lsf with `--app-url`. So
|
|
488
|
+
`application_url`, often your production backend or a dead tunnel, never gets one by accident.
|
|
489
|
+
|
|
490
|
+
The secret is the app's. Without the app (no `--app`, and run outside its folder), its `.env`
|
|
491
|
+
is not read: give `--client-secret` or set `SHOPIFY_API_SECRET`. Without either, lsf signs with
|
|
492
|
+
its own secret, and the backend answers `401`. A `401` or `403` says which secret lsf signed
|
|
493
|
+
with: `failed: answered 401 (1 attempt): lsf signed it with lsf-local-client-secret, its own:
|
|
494
|
+
give lsf the backend's secret (--client-secret, or --app for the SHOPIFY_API_SECRET of its
|
|
495
|
+
.env)`.
|
|
496
|
+
|
|
497
|
+
| Option | Environment variable | What it does |
|
|
498
|
+
|---|---|---|
|
|
499
|
+
| `--topic <topic>` | | The topic, as Shopify writes it (`orders/create`) or as the GraphQL enum does (`ORDERS_CREATE`). Every topic of the API version (`--api-version`) and the compliance topics (`customers/data_request`, `customers/redact`, `shop/redact`). A topic close to an existing one is suggested. |
|
|
500
|
+
| `--address <url>` | | Where to send it: `https://…`, or `http://localhost:<port>/<path>` (any address of this machine: `localhost`, `*.localhost`, `127.0.0.0/8`, `::1`). |
|
|
501
|
+
| `--api-version <version>` | | The version the payload is built in: `2026-10` (the default) or another lsf knows (`2027-01`), for `--address` and the subscriptions of `shopify.app.toml` alike, as Shopify's CLI sends the sample of that version. A subscription whose `api_version` differs gets it all the same, and a `note:` says so. A topic the version does not have is refused. |
|
|
502
|
+
| `--delivery-method <method>` | | `http`, `google-pub-sub` or `event-bridge`; without it, what the address says. lsf delivers over HTTP only: the other two are refused. |
|
|
503
|
+
| `--client-secret <secret>` | `LSF_CLIENT_SECRET` | The secret of the signature. Default: `SHOPIFY_API_SECRET` of the environment, else of the app's `.env`, else lsf's local secret, `lsf-local-client-secret`. |
|
|
504
|
+
| `--app <dir>`, `--path <dir>` | `LSF_APP` | The app. Defaults to the app the current folder is in; may be left out with `--address`. |
|
|
505
|
+
| `--app-url <url>` | `LSF_APP_URL` | Where the backend of the app listens. Without it, `application_url`, when it is on this machine. |
|
|
506
|
+
| `--format text` | | Default. The request it sent, the answer, the state. |
|
|
507
|
+
| `--format json` | | Prints the deliveries as JSON: `{ok, deliveries: [...]}`, each as [`GET /__lsf/webhooks`](control-api.md#webhook-deliveries) lists it (`topic`, `url`, `request`, `attempts`, `state`, `reason`, …). `--json` and `-j`, from before `--format`, still work. |
|
|
508
|
+
|
|
509
|
+
lsf builds the payloads of the topics of products, collections, customers, orders, draft
|
|
510
|
+
orders, fulfillments, refunds, inventory items and levels, locations, metaobjects, discounts,
|
|
511
|
+
the shop, the app (`app/uninstalled`, `app/scopes_update`, `app_subscriptions/update`), bulk
|
|
512
|
+
operations, and the compliance topics. Another topic is refused, rather than sent with a body
|
|
513
|
+
made up for it.
|
|
514
|
+
|
|
515
|
+
Exits with `1` when a delivery failed: an answer out of `2xx` (a redirect included, as on
|
|
516
|
+
Shopify), no answer within 5 seconds, a backend that is not listening. Exits with `2` when
|
|
517
|
+
nothing could be sent: an unknown topic, a topic lsf does not build, no address and no
|
|
518
|
+
subscription to the topic, a Pub/Sub or EventBridge address, an address on another machine
|
|
519
|
+
that `--app-url` or `--address` does not name.
|
|
520
|
+
|
|
521
|
+
## render
|
|
522
|
+
|
|
523
|
+
`npx local-storefront render <path>`
|
|
524
|
+
|
|
525
|
+
Prints the HTML of one page, without a server.
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
npx local-storefront render '/collections/all?sort_by=price-ascending' --strict > collection.html
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
| Option | What it does |
|
|
532
|
+
|---|---|
|
|
533
|
+
| `<path>` | The path, with its query string. Default `/`. |
|
|
534
|
+
| `--section-id <id>` | Renders one section, as the Section Rendering API does. |
|
|
535
|
+
| `--host <host>` | The host of the URLs in the page. Default `localhost:9292`. |
|
|
536
|
+
| `--strict` | Exits with `1` when the page has Liquid errors, or when nothing is at the path (the theme's 404 page). |
|
|
537
|
+
| `--customer <email>` | Renders with this customer logged in: an email of the data, `default` for the first customer, or `none`. Also `LSF_CUSTOMER`. |
|
|
538
|
+
| `--cart <variant>[=<quantity>]` | Puts a variant in the cart: its SKU, its id, or a product handle for its first variant. Repeatable: `--cart TEE-BLK-M=2 --cart ceramic-mug`. |
|
|
539
|
+
| `--session <json>` | The session as `PUT /__lsf/session` takes it (`customer`, `cart`, `country`, `company_location`), as JSON or the path of a JSON file. `--customer` and `--cart` apply on top. A name the data does not have exits with `2`. |
|
|
540
|
+
| `--format github` | Prints each Liquid error as a [GitHub Actions annotation](#github-annotations), on stderr, with its file and line. Default `text`. |
|
|
541
|
+
|
|
542
|
+
`render` answers a path as `serve` does. A `section_id` or `sections` in the query renders those
|
|
543
|
+
sections, as the Section Rendering API does. `/account` (lsf's page of the customer account),
|
|
544
|
+
`/recommendations/products` and `/search/suggest` are answered by the server, as `serve`
|
|
545
|
+
answers them.
|
|
546
|
+
|
|
547
|
+
The HTML goes to stdout. On stderr: each Liquid error and each warning, then a summary.
|
|
548
|
+
|
|
549
|
+
```text
|
|
550
|
+
Liquid error (layout/theme line 34): divided by 0
|
|
551
|
+
status 200 · template product · 1 Liquid error · 0 warnings
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
With `--strict`, a path with nothing at it adds `error: nothing at /x: the page is the theme's
|
|
555
|
+
404`.
|
|
556
|
+
|
|
557
|
+
## profile
|
|
558
|
+
|
|
559
|
+
`npx local-storefront profile <path>`
|
|
560
|
+
|
|
561
|
+
Prints what the render of one page spends its time on: sections, blocks, snippets. See
|
|
562
|
+
[performance](performance.md#profile-a-page).
|
|
563
|
+
|
|
564
|
+
| Option | What it does |
|
|
565
|
+
|---|---|
|
|
566
|
+
| `<path>` | The path, with its query string. Default `/`. |
|
|
567
|
+
| `--points` | Counts in points, what the page would cost Shopify, instead of time. |
|
|
568
|
+
| `--cost <kind>=<points>,...` | Changes what a kind of thing costs in points. |
|
|
569
|
+
| `--lines` | Adds every tag and every output, by file and line. |
|
|
570
|
+
| `--all` | Shows every row. Without it, the smallest rows are summed up. |
|
|
571
|
+
| `--runs <n>` | Renders the page `n` times and keeps the one in the middle. Default `15`. |
|
|
572
|
+
| `--section-id <id>` | Profiles one section alone. |
|
|
573
|
+
| `--json` | Prints the profile in speedscope's format, for a flame graph. |
|
|
574
|
+
|
|
575
|
+
## validate
|
|
576
|
+
|
|
577
|
+
`npx local-storefront validate`
|
|
578
|
+
|
|
579
|
+
Checks the store data and explains every problem, its checkout scenarios included.
|
|
580
|
+
|
|
581
|
+
The data is the one `app dev` reads: `--data`, or the folder's `shopify-local/`, or the
|
|
582
|
+
app's (`<app>/shopify-local`), or the built-in demo store. In a folder that is not a theme (an
|
|
583
|
+
app, such as [the checkout playground](https://github.com/0xtlt/local-storefront/tree/main/examples/checkout-playground)), it checks the data
|
|
584
|
+
alone, with the store's own languages; an app without any is checked with the demo store, as its
|
|
585
|
+
checkout runs. A folder that is neither a theme nor an app, and has no store data, is an error.
|
|
586
|
+
|
|
587
|
+
In a Shopify app (or with `--app`), it checks the app too: what its Functions and UI extensions
|
|
588
|
+
get wrong (no build, an API version lsf does not run, an unknown target, an `owner_type`), and
|
|
589
|
+
its theme app extensions as `app build` checks them (`app: badges: extensions/badges/blocks/stars.liquid:
|
|
590
|
+
a block needs a {% schema %}`), and exits with `1` on an error of it.
|
|
591
|
+
|
|
592
|
+
| Option | What it does |
|
|
593
|
+
|---|---|
|
|
594
|
+
| `--app <dir>` | The app to check too (`LSF_APP`). Defaults to the app the current folder is in. |
|
|
595
|
+
| `--format text` | Default. One block per problem, for a person. |
|
|
596
|
+
| `--format json` | One JSON document, for a tool: `errors`, `warnings` and `diagnostics` are those of the data and of the app together (an app's has the code `app` and its extension as `file`), `app` has the app's own when there is one. |
|
|
597
|
+
| `--format github` | One [GitHub Actions annotation](#github-annotations) per problem, on stdout: the file of the store data, and the line of the value. |
|
|
598
|
+
|
|
599
|
+
The text format:
|
|
600
|
+
|
|
601
|
+
```text
|
|
602
|
+
error[unknown_field]: unknown field "titel"
|
|
603
|
+
--> products/shirt.json at /titel
|
|
604
|
+
hint: did you mean "title"?
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
The JSON format:
|
|
608
|
+
|
|
609
|
+
```json
|
|
610
|
+
{
|
|
611
|
+
"ok": false,
|
|
612
|
+
"errors": 1,
|
|
613
|
+
"warnings": 0,
|
|
614
|
+
"data": "./shopify-local",
|
|
615
|
+
"diagnostics": [
|
|
616
|
+
{
|
|
617
|
+
"severity": "error",
|
|
618
|
+
"code": "unknown_field",
|
|
619
|
+
"file": "products/shirt.json",
|
|
620
|
+
"path": "/titel",
|
|
621
|
+
"message": "unknown field \"titel\"",
|
|
622
|
+
"hint": "did you mean \"title\"?"
|
|
623
|
+
}
|
|
624
|
+
],
|
|
625
|
+
"app": null
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
`data` is the folder read, and `file` is in it. `path` is a JSON Pointer into `file`. `app` is
|
|
630
|
+
`null` outside a Shopify app. The codes are listed in the
|
|
631
|
+
[data guide](data-format.md#error-codes). Exits with `1` when there is an error; warnings
|
|
632
|
+
alone exit with `0`.
|
|
633
|
+
|
|
634
|
+
## check
|
|
635
|
+
|
|
636
|
+
`npx local-storefront check`
|
|
637
|
+
|
|
638
|
+
Parses every Liquid file of the theme and reports what would not render on Shopify, or what
|
|
639
|
+
Shopify refuses on upload:
|
|
640
|
+
|
|
641
|
+
- syntax errors, unknown filters, and what Shopify's strict parser rejects;
|
|
642
|
+
- the `{% schema %}` of a section or a theme block that Shopify refuses: JSON that does not
|
|
643
|
+
parse, a setting type Shopify does not have, a setting without its `id` or `label`, two
|
|
644
|
+
settings with one id, a `select` or `radio` whose `default` is not one of its `options`, a
|
|
645
|
+
`range` whose `default` is outside `min` and `max` or not one of its steps, or that has more
|
|
646
|
+
than 101 steps (`invalid_schema`, `unknown_setting_type`, `duplicate_setting_id`);
|
|
647
|
+
- what the theme names but does not have: a snippet of `render` or `include`
|
|
648
|
+
(`missing_snippet`), a section of `{% section %}` or of a JSON template (`missing_section`), a
|
|
649
|
+
group of `{% sections %}` (`missing_section_group`), a theme block of a schema, of a JSON
|
|
650
|
+
template, of a section group or of `{% content_for 'block', type: … %}` (`missing_block`), a
|
|
651
|
+
key of the `t` filter that the default locale does not have (`missing_translation`), a `t:` key
|
|
652
|
+
of a schema that the default schema locale does not have (`missing_schema_translation`);
|
|
653
|
+
- as a warning, a file of `assets/` that `asset_url` names and the theme does not have
|
|
654
|
+
(`missing_asset`).
|
|
655
|
+
|
|
656
|
+
Only names written as literals are checked. It reads `layout/`, `templates/`, `sections/`,
|
|
657
|
+
`blocks/`, `snippets/`, and the `liquid` settings of the JSON templates, section groups and
|
|
658
|
+
`config/settings_data.json`. Guide: [strict Liquid](strict-liquid.md).
|
|
659
|
+
|
|
660
|
+
| Option | What it does |
|
|
661
|
+
|---|---|
|
|
662
|
+
| `--fix` | Rewrites what the strict parser rejects. A file is written only if it has not changed since it was read. Run twice, the second run writes nothing. |
|
|
663
|
+
| `--app <dir>` | Checks the app blocks and app embed blocks the theme names against the [theme app extensions](storefront.md#theme-app-extensions) of this app. A block that renders nothing is a problem, and the check exits with `1`: a block the app does not have (with the name it probably meant), an app embed block in a section, a block its `enabled_on` or `disabled_on` keeps out of the template or section group. A block of another app is a note. Without it, a note says how many the theme names, not checked. |
|
|
664
|
+
| `--format text` | Default. One block per problem, for a person. |
|
|
665
|
+
| `--format json` | One JSON document, for a tool. |
|
|
666
|
+
| `--format github` | One [GitHub Actions annotation](#github-annotations) per problem, on stdout. |
|
|
667
|
+
|
|
668
|
+
### The text format
|
|
669
|
+
|
|
670
|
+
Each problem is a line `<file>:<line>: <message>`, followed by indented details. A warning,
|
|
671
|
+
which is not counted as a problem, has `warning: ` before its message, and a note `note: `
|
|
672
|
+
(`"severity": "info"` in JSON).
|
|
673
|
+
|
|
674
|
+
| Detail | Meaning |
|
|
675
|
+
|---|---|
|
|
676
|
+
| `was:` | The markup as written. Long markup shows only the words that change. |
|
|
677
|
+
| `fix:` | The rewrite `--fix` writes. After `--fix`, it is `now:`. |
|
|
678
|
+
| `before it:` | Tags the fix adds on the lines above, separated by `;`. |
|
|
679
|
+
| `its body goes in:` | The `if` the fix puts the body of a `when` in. |
|
|
680
|
+
| `changes the output:` | What renders differently with the fix. Absent when the output stays the same. |
|
|
681
|
+
| `left out:` | Arguments the fix drops, which the snippet does not read. |
|
|
682
|
+
| `no automatic fix:` | Why there is no fix. |
|
|
683
|
+
|
|
684
|
+
For a `liquid` setting, the location is `<file>:<line>: <path of the setting>`, and
|
|
685
|
+
`, line <n>` when the problem is not on the first line of the value. The summary goes to
|
|
686
|
+
stderr.
|
|
687
|
+
|
|
688
|
+
### The JSON format
|
|
689
|
+
|
|
690
|
+
```json
|
|
691
|
+
{
|
|
692
|
+
"ok": false,
|
|
693
|
+
"files": 191,
|
|
694
|
+
"problems": 2,
|
|
695
|
+
"fixable": 2,
|
|
696
|
+
"fixed": 0,
|
|
697
|
+
"changes_output": 1,
|
|
698
|
+
"diagnostics": [
|
|
699
|
+
{
|
|
700
|
+
"severity": "error",
|
|
701
|
+
"code": "strict_liquid_rejected",
|
|
702
|
+
"file": "snippets/card.liquid",
|
|
703
|
+
"line": 1,
|
|
704
|
+
"message": "Shopify's strict Liquid parser rejects this {% render %}: Expected end_of_string but found pipe",
|
|
705
|
+
"subject": "{% render %}",
|
|
706
|
+
"markup": "'image', alt: image.alt | default: shop.name",
|
|
707
|
+
"fix": {
|
|
708
|
+
"written": false,
|
|
709
|
+
"markup": "'image', alt: image_alt",
|
|
710
|
+
"before": ["assign image_alt = image.alt | default: shop.name"],
|
|
711
|
+
"changes_output": "`alt` gets `image.alt | default: shop.name`"
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
"severity": "error",
|
|
716
|
+
"code": "strict_liquid_rejected",
|
|
717
|
+
"file": "templates/index.json",
|
|
718
|
+
"line": 3,
|
|
719
|
+
"setting": "sections.custom.settings.custom_liquid",
|
|
720
|
+
"setting_line": 2,
|
|
721
|
+
"message": "Shopify's strict Liquid parser rejects this {{ }}: Expected end_of_string but found id",
|
|
722
|
+
"subject": "{{ }}",
|
|
723
|
+
"markup": "product.title product.vendor",
|
|
724
|
+
"fix": { "written": false, "markup": "product.title" }
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
}
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
| Key | Meaning |
|
|
731
|
+
|---|---|
|
|
732
|
+
| `ok` | `true` when no problem is left. |
|
|
733
|
+
| `files` | How many files were read. |
|
|
734
|
+
| `problems` | Problems left: not fixed, or without a fix. |
|
|
735
|
+
| `fixable` | Problems left that `--fix` would fix. |
|
|
736
|
+
| `fixed` | Problems `--fix` fixed in this run. |
|
|
737
|
+
| `changes_output` | Fixes, written or not, that change what renders. |
|
|
738
|
+
|
|
739
|
+
Each diagnostic has a `severity` (`error`, or `warning` for what does not count as a
|
|
740
|
+
problem), a `code`, a `file`, a `line` when there is one, and a `message`.
|
|
741
|
+
|
|
742
|
+
Without `--fix`, the report is a dry run: each `fix` is what `--fix` writes. After `--fix`, the
|
|
743
|
+
problems it fixed stay in `diagnostics`, with `fix.written: true` and still `error`; `problems`
|
|
744
|
+
counts the others.
|
|
745
|
+
|
|
746
|
+
| `code` | What it is |
|
|
747
|
+
|---|---|
|
|
748
|
+
| `strict_liquid_rejected` | Markup Shopify's strict parser rejects. |
|
|
749
|
+
| `strict_liquid_reinterpreted` | Markup Shopify's strict parser reads differently from today. |
|
|
750
|
+
| `liquid_syntax` | A file that does not parse at all. |
|
|
751
|
+
| `unknown_filter` | A filter local-storefront does not know. Either a typo: Shopify skips an unknown filter, and the value comes out unchanged. A filter of Shopify one or two letters away, with the same letters, or a name of another template language (`uppercase`) is in the `hint`. Or a filter of Shopify that local-storefront lacks: [report it](https://github.com/0xtlt/local-storefront/issues). |
|
|
752
|
+
| `block_outside_template` | `{% block %}` outside `layout/` and `templates/` (with `--preview liquid-july-26`). |
|
|
753
|
+
| `invalid_json` | A JSON file that does not parse. |
|
|
754
|
+
| `invalid_schema` | A `{% schema %}` Shopify refuses: JSON that does not parse, a setting without a required key (`id`, `label`, `options` of a `select`, `min`, `max` and `default` of a `range`), a `default` its setting does not take. A section or a block whose schema does not parse loses the defaults of its settings. |
|
|
755
|
+
| `unknown_setting_type` | A setting of a schema whose `type` Shopify does not have. The `hint` gives the closest one. |
|
|
756
|
+
| `duplicate_setting_id` | Two settings of one schema with the same `id`. |
|
|
757
|
+
| `missing_block` | A theme block the theme names and `blocks/` does not have: in the `blocks` or the `presets` of a schema, in a JSON template or a section group, or in `{% content_for 'block', type: '…' %}`. A section with blocks of its own (with a `name`) names its own types. |
|
|
758
|
+
| `missing_schema_translation` | A `t:` key of a schema that the default schema locale (`locales/en.default.schema.json`) does not have: the theme editor shows the key. |
|
|
759
|
+
| `missing_asset` | A warning: `'x.js' \| asset_url` names a file `assets/` does not have (nor `assets/x.js.liquid`). The URL leads to nothing. |
|
|
760
|
+
| `app_block` | With `--app`, an app block that renders nothing (an error), or a block of another app (a note). Without `--app`, a note. |
|
|
761
|
+
| `fix_fallback` | A warning: what the code meant could not be written for this file, so only the fixes that keep the output were. |
|
|
762
|
+
| `classic_customer_accounts` | A warning: the theme has `templates/customers`, the pages of classic customer accounts, which lsf never renders (`/account` stands for Shopify's new customer accounts). The theme is fine on Shopify. |
|
|
763
|
+
|
|
764
|
+
The diagnostics of the strict parser have more keys:
|
|
765
|
+
|
|
766
|
+
| Key | Meaning |
|
|
767
|
+
|---|---|
|
|
768
|
+
| `setting`, `setting_line` | For Liquid in a JSON file: the setting, as its keys joined with dots, and the line in its value. `line` is then where the value starts in the file. |
|
|
769
|
+
| `subject` | `{% <tag> %}`, or `{{ }}` for an output. |
|
|
770
|
+
| `markup` | The markup as written. |
|
|
771
|
+
| `fix.written` | Whether `--fix` wrote the fix. |
|
|
772
|
+
| `fix.markup` | What replaces the markup, or the whole tag when the fix rewrites the tags around it (a `case` that becomes `if`). |
|
|
773
|
+
| `fix.before` | Tags the fix puts on the lines above, without their delimiters. |
|
|
774
|
+
| `fix.body_in_if` | The condition of the `if` the body of a `when` goes in. |
|
|
775
|
+
| `fix.left_out` | `{ variables, snippet }`: arguments the fix drops, which the snippet does not read. |
|
|
776
|
+
| `fix.changes_output` | What renders differently with the fix. Absent when the output stays the same. |
|
|
777
|
+
| `no_fix` | Why there is no fix, when there is none. |
|
|
778
|
+
| `hint` | For an `unknown_filter`, the filter of Shopify it is probably a typo of: `did you mean "upcase"?`. Absent when none is close. |
|
|
779
|
+
|
|
780
|
+
Exits with `1` when a problem is left, whatever the format.
|
|
781
|
+
|
|
782
|
+
### GitHub annotations
|
|
783
|
+
|
|
784
|
+
`--format github` prints each problem as a workflow command of GitHub Actions, which shows it on
|
|
785
|
+
its line in the pull request:
|
|
786
|
+
|
|
787
|
+
```text
|
|
788
|
+
::error file=sections/hero.liquid,line=40,title=lsf check%3A unknown_setting_type::setting "align" is of type "selct", which Shopify does not have: Shopify refuses the file%0Ahint: did you mean "select"?
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
The file is relative to the working directory, where the workflow runs. `validate` and
|
|
792
|
+
`render` take `--format github` too: `validate` names the file of the store data and the line
|
|
793
|
+
of the value, `render` the file and the line of each Liquid error.
|
|
794
|
+
|
|
795
|
+
## fmt
|
|
796
|
+
|
|
797
|
+
`npx local-storefront fmt [paths]`
|
|
798
|
+
|
|
799
|
+
Formats Liquid files as Prettier does with Shopify's Liquid plugin. Guide:
|
|
800
|
+
[formatting](formatting.md).
|
|
801
|
+
|
|
802
|
+
| Option | What it does |
|
|
803
|
+
|---|---|
|
|
804
|
+
| `[paths]` | Files and folders to format. Default: the theme folder. |
|
|
805
|
+
| `--check` | Writes nothing. Lists the files that are not formatted on stdout, the summary on stderr, and exits with `1` if there are any. |
|
|
806
|
+
| `--stdin` | Formats the standard input and prints it. For an editor. |
|
|
807
|
+
| `--stdin-filepath <path>` | With `--stdin`: the file the source comes from, for its configuration and `.prettierignore`. |
|
|
808
|
+
| `--option <name>=<value>`, `-o` | Sets a Prettier option: `-o printWidth=100`. Wins over the configuration files. |
|
|
809
|
+
| `--no-config` | Does not read `.prettierrc`, `.editorconfig` nor `.prettierignore`. |
|
|
810
|
+
|
|
811
|
+
A file that cannot be read, written or formatted is reported, the others are formatted, and
|
|
812
|
+
the command exits with `2`.
|
|
813
|
+
|
|
814
|
+
## init
|
|
815
|
+
|
|
816
|
+
`npx local-storefront init`
|
|
817
|
+
|
|
818
|
+
Creates the data folder with a demo store, JSON Schemas for editors, and the data guide as
|
|
819
|
+
`README.md`. For the checkout of a Shopify app, [`app init`](#app-init) writes the
|
|
820
|
+
scenarios.
|
|
821
|
+
|
|
822
|
+
It keeps the files that exist, so it is safe to run again, and refreshes `schema/`. It prints
|
|
823
|
+
on stderr, and exits with `0`:
|
|
824
|
+
|
|
825
|
+
```text
|
|
826
|
+
./shopify-local: wrote 16 files, kept 0 existing ones, refreshed schema/
|
|
827
|
+
next: the `serve` command shows the theme, and `validate` checks the data after you edit it
|
|
828
|
+
for a Shopify app: `app init --app <dir>` writes checkout scenarios to try its Functions and extensions with
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
| Option | What it does |
|
|
832
|
+
|---|---|
|
|
833
|
+
| `--force` | Overwrites files that exist, your edits included. |
|
|
834
|
+
|
|
835
|
+
## schema
|
|
836
|
+
|
|
837
|
+
`npx local-storefront schema [kind]`
|
|
838
|
+
|
|
839
|
+
Prints the JSON Schema of the store data.
|
|
840
|
+
|
|
841
|
+
| Option | What it does |
|
|
842
|
+
|---|---|
|
|
843
|
+
| `[kind]` | `store` (default, a root data file), `product`, `collection`, `page`, `blog`, `customer`, `menu`, `session` or `checkout-scenario` (a file of `checkouts/`). |
|
|
844
|
+
| `--out <dir>` | Writes every schema into the folder as `<kind>.schema.json`. |
|
|
845
|
+
|
|
846
|
+
## routes
|
|
847
|
+
|
|
848
|
+
`npx local-storefront routes`
|
|
849
|
+
|
|
850
|
+
Lists the URLs the store data gives a page to, one per line. Each one renders: a loop of
|
|
851
|
+
`render --strict` over them checks every page. `/account`, a page of lsf's rather than of the
|
|
852
|
+
theme, is not listed; `GET /__lsf/status` lists the same `routes`.
|
|
853
|
+
|
|
854
|
+
| Option | What it does |
|
|
855
|
+
|---|---|
|
|
856
|
+
| `--all` | Adds the states a page has beyond its URL: a search that finds the first product (`/search?q=…`), the sections the theme's JavaScript fetches when the theme shows them (product recommendations of the product page, `/recommendations/products?…§ion_id=…`; predictive search, `/search/suggest?…§ion_id=predictive-search`), and then every route again in each other language of the store (`/fr/…`). |
|
|
857
|
+
|
|
858
|
+
```bash
|
|
859
|
+
npx local-storefront routes --all > routes.txt
|
|
860
|
+
while read -r route; do
|
|
861
|
+
npx local-storefront render "$route" --strict > /dev/null || echo "failed: $route"
|
|
862
|
+
done < routes.txt
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
## docs
|
|
866
|
+
|
|
867
|
+
`npx local-storefront docs`
|
|
868
|
+
|
|
869
|
+
Prints the data guide, with the reference of every field.
|
|
870
|
+
|
|
871
|
+
| Option | What it does |
|
|
872
|
+
|---|---|
|
|
873
|
+
| `--reference` | Prints only the field reference. |
|