local-storefront 0.2.0 → 0.3.0-rc.2
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 +1384 -0
- package/README.md +14 -9
- package/bin/lsf.js +4 -1
- package/docs/README.md +59 -0
- package/docs/admin-reference.md +1412 -0
- package/docs/admin.md +1751 -0
- package/docs/agents.md +366 -0
- package/docs/architecture.md +303 -0
- package/docs/checkout.md +1612 -0
- package/docs/cli.md +917 -0
- package/docs/compatibility.md +502 -0
- package/docs/control-api.md +251 -0
- package/docs/data-format.md +1161 -0
- package/docs/data-reference.md +1272 -0
- package/docs/formatting.md +119 -0
- package/docs/performance.md +267 -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 +251 -0
- package/docs/strict-liquid.md +178 -0
- package/docs/testing.md +590 -0
- package/llms.txt +39 -0
- package/package.json +18 -9
package/docs/agents.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
How an AI agent, or any tool, drives local-storefront: the commands to run, what they print, and
|
|
4
|
+
how to read the result without guessing. Every command is non-interactive and exits with a code
|
|
5
|
+
a script can test.
|
|
6
|
+
|
|
7
|
+
## Before anything
|
|
8
|
+
|
|
9
|
+
- Run `npx local-storefront <command>` in a theme folder. Never `npx lsf`: that is another
|
|
10
|
+
package on npm.
|
|
11
|
+
- Point at the theme with `--theme <dir>` and at the data with `--data <dir>` when the current
|
|
12
|
+
folder is not the theme.
|
|
13
|
+
- Exit codes: `0` nothing to report, `1` something to fix, `2` the command could not run. See
|
|
14
|
+
the [CLI reference](cli.md#exit-codes).
|
|
15
|
+
- Results go to stdout, messages and summaries to stderr.
|
|
16
|
+
|
|
17
|
+
### What each command prints
|
|
18
|
+
|
|
19
|
+
| Command | For a tool |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `validate`, `check` (`--fix` too), `app check`, `app function run` | `--format json`: one JSON document on stdout. |
|
|
22
|
+
| `app webhook trigger` | `--format json` (`--json` is the same). |
|
|
23
|
+
| `render <path>` | The HTML on stdout. On stderr, each Liquid error, then `status 200 · template product · 0 Liquid errors · 0 warnings`. |
|
|
24
|
+
| `routes` | One path per line. |
|
|
25
|
+
| `fmt --check` | One file to format per line; the summary on stderr. |
|
|
26
|
+
| `profile <path>` | Text. `--json` is a speedscope profile for a flame graph, not a report. |
|
|
27
|
+
| `schema [kind]` | A JSON Schema. |
|
|
28
|
+
| `init`, `app init` | Text, on stderr. They take no `--format`: `init --format json` suggests `--force`, which overwrites the data. Do not follow it. |
|
|
29
|
+
| `app function schema` | On stderr, the file it wrote (`wrote …/schema.graphql: the payment_customization API, 2027-01`). |
|
|
30
|
+
| `serve`, `app dev` | Its lines on stderr; the state as JSON at `GET /__lsf/status`. |
|
|
31
|
+
|
|
32
|
+
## Tasks
|
|
33
|
+
|
|
34
|
+
### Set up a theme
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install --save-dev local-storefront
|
|
38
|
+
npx local-storefront init
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`init` writes `shopify-local/` with a demo store, and prints on stderr
|
|
42
|
+
`./shopify-local: wrote 16 files, kept 0 existing ones, refreshed schema/`. It keeps the
|
|
43
|
+
files that exist, so it is safe to run again (`wrote 0 files, kept 16 existing ones`).
|
|
44
|
+
`--force` overwrites them. Exit `0`.
|
|
45
|
+
|
|
46
|
+
To use your own products instead of the demo's, delete `shopify-local/products/*.json`,
|
|
47
|
+
`customers.json` and `gift_cards.json`, write yours (next task), then run
|
|
48
|
+
`npx local-storefront validate --format json`. The demo's collections, menus and subscriptions
|
|
49
|
+
still name its products: each one is an `unknown_product` or an `unknown_reference`, at its
|
|
50
|
+
`file` (`collections.json`, `menus.json`, `store.json`) and `path`. Replace or remove each.
|
|
51
|
+
Remove from the last `path` to the first: removing an item of a list moves the `path` of the
|
|
52
|
+
items after it. A collection or a selling-plan group may be left with no products
|
|
53
|
+
(`"products": []`).
|
|
54
|
+
|
|
55
|
+
The demo's Apparel, Home goods, Accessories and Sale collections pick products by `type`
|
|
56
|
+
(`Apparel`, `Home`, `Accessories`) and by the tag `sale`: give yours a `type` and `tags`, or
|
|
57
|
+
change the `rules` of each collection. Otherwise they are empty, and every check still passes.
|
|
58
|
+
The shop's name is `shop.name` in `store.json` (`Local Supply Co.`), and the pages and articles
|
|
59
|
+
of `pages.json` and `blogs.json` are the demo's too.
|
|
60
|
+
|
|
61
|
+
### Write store data
|
|
62
|
+
|
|
63
|
+
One file per product, `shopify-local/products/trail-runner.json`:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"title": "Trail Runner",
|
|
68
|
+
"price": "129.00",
|
|
69
|
+
"compare_at_price": "149.00",
|
|
70
|
+
"options": ["Size"],
|
|
71
|
+
"variants": [
|
|
72
|
+
{ "options": ["42"], "sku": "TRAIL-42", "inventory_quantity": 5 },
|
|
73
|
+
{ "options": ["43"], "sku": "TRAIL-43", "inventory_quantity": 0 }
|
|
74
|
+
],
|
|
75
|
+
"images": ["products/trail-runner.jpg"]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The customers, `shopify-local/customers.json`:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"customers": [{
|
|
84
|
+
"email": "alex.martin@example.com",
|
|
85
|
+
"first_name": "Alex",
|
|
86
|
+
"addresses": [{ "address1": "1 Main St", "city": "New York", "country_code": "US", "zip": "10001" }],
|
|
87
|
+
"orders": [{ "line_items": [{ "variant": "TRAIL-42", "quantity": 1 }] }]
|
|
88
|
+
}]
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Money is a decimal string (`"129.00"`) or cents (`12900`), never a JSON float. The handle comes
|
|
93
|
+
from the file name (`trail-runner`). An image that is not in `files/` renders as a placeholder
|
|
94
|
+
of its size: test data needs no image files. The rest, in the data guide:
|
|
95
|
+
[conventions](data-format.md#conventions), [products](data-format.md#products),
|
|
96
|
+
[customers](data-format.md#customers-and-sessions), [images](data-format.md#images-and-files),
|
|
97
|
+
[error codes](data-format.md#error-codes).
|
|
98
|
+
|
|
99
|
+
### Check the store data
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx local-storefront validate --format json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"ok": false,
|
|
108
|
+
"errors": 1,
|
|
109
|
+
"warnings": 0,
|
|
110
|
+
"data": "./shopify-local",
|
|
111
|
+
"diagnostics": [
|
|
112
|
+
{
|
|
113
|
+
"severity": "error",
|
|
114
|
+
"code": "unknown_field",
|
|
115
|
+
"file": "products/shirt.json",
|
|
116
|
+
"path": "/titel",
|
|
117
|
+
"message": "unknown field \"titel\"",
|
|
118
|
+
"hint": "did you mean \"title\"?"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"app": null
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`path` is a JSON Pointer into `file`, in the folder `data`. Apply the `hint` when there is one,
|
|
126
|
+
then run the command again until `ok` is `true`. `app` is `null` outside a Shopify app. The
|
|
127
|
+
codes: [data guide](data-format.md#error-codes). The format of every field:
|
|
128
|
+
`npx local-storefront docs`, or `npx local-storefront schema <kind>` for a JSON Schema.
|
|
129
|
+
|
|
130
|
+
Fix the errors in `products/` and `customers.json` first. A product or a customer with a value
|
|
131
|
+
that cannot be read (a `wrong_type`, an `invalid_value`, an `invalid_format`, a
|
|
132
|
+
`missing_field`) is left out, so each reference to it is an error too, in its own file: in the
|
|
133
|
+
demo store, one float price gives 4 errors in 4 files. Run `validate` again before you edit a
|
|
134
|
+
reference.
|
|
135
|
+
|
|
136
|
+
### Check the theme
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npx local-storefront check --format json
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"ok": false,
|
|
145
|
+
"files": 191,
|
|
146
|
+
"problems": 1,
|
|
147
|
+
"fixable": 1,
|
|
148
|
+
"fixed": 0,
|
|
149
|
+
"changes_output": 1,
|
|
150
|
+
"diagnostics": [
|
|
151
|
+
{
|
|
152
|
+
"severity": "error",
|
|
153
|
+
"code": "strict_liquid_rejected",
|
|
154
|
+
"file": "snippets/card.liquid",
|
|
155
|
+
"line": 1,
|
|
156
|
+
"message": "Shopify's strict Liquid parser rejects this {% render %}: Expected end_of_string but found pipe",
|
|
157
|
+
"subject": "{% render %}",
|
|
158
|
+
"markup": "'image', alt: image.alt | default: shop.name",
|
|
159
|
+
"fix": {
|
|
160
|
+
"written": false,
|
|
161
|
+
"markup": "'image', alt: image_alt",
|
|
162
|
+
"before": ["assign image_alt = image.alt | default: shop.name"],
|
|
163
|
+
"changes_output": "`alt` gets `image.alt | default: shop.name`"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
This is the dry run: each `fix` is exactly what `--fix` writes. A diagnostic with `no_fix`, or
|
|
171
|
+
with neither key (an `unknown_filter`, which may have a `hint`), is not fixed by `--fix`: see
|
|
172
|
+
the rules of the next task.
|
|
173
|
+
Exit `1` while a problem is left. Every key and code: [CLI reference](cli.md#the-json-format).
|
|
174
|
+
|
|
175
|
+
`check` reads the files and renders none: a Liquid error at render time (a division by zero)
|
|
176
|
+
shows only with `render --strict`.
|
|
177
|
+
|
|
178
|
+
### Fix what Shopify's strict Liquid parser rejects
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npx local-storefront check --fix --format json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Rules for an agent:
|
|
185
|
+
|
|
186
|
+
- Run it on a clean working tree, so that `git diff` shows exactly what it wrote.
|
|
187
|
+
- The fixed problems stay in `diagnostics`, with `fix.written: true`; their `severity` is still
|
|
188
|
+
`error`. `problems` counts what is left: the errors without `fix.written: true` (a `warning`
|
|
189
|
+
never counts).
|
|
190
|
+
- A fix with `changes_output` changes the rendered HTML on purpose: the code now does what it
|
|
191
|
+
says. Report each one to the person, with its `file` and `line`. Do not revert it to silence
|
|
192
|
+
the change.
|
|
193
|
+
- Two `no_fix` reasons have a rewrite in [strict Liquid](strict-liquid.md#fix-by-hand):
|
|
194
|
+
`` `a.b` is not a name the strict parser reads `` and `` `=<` is read as an operator… ``.
|
|
195
|
+
Apply the rewrite, and report it as you report a `changes_output`: the page changes. Any other
|
|
196
|
+
`no_fix` needs a person's decision. Do not invent a rewrite: report it with its `file`,
|
|
197
|
+
`line` and `no_fix`.
|
|
198
|
+
- An `unknown_filter` with a `hint` is a typo (`uppercas`, `did you mean "upcase"?`): apply
|
|
199
|
+
the hint, and report it, since the page changes. One without a `hint` may be a filter that
|
|
200
|
+
local-storefront lacks: report it.
|
|
201
|
+
- It is safe to run twice: the second run writes nothing.
|
|
202
|
+
- Run `npx local-storefront check --format json` again: `problems` is `0`, or every problem left
|
|
203
|
+
is reported to the person.
|
|
204
|
+
|
|
205
|
+
Guide: [strict Liquid](strict-liquid.md).
|
|
206
|
+
|
|
207
|
+
### Render a page
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npx local-storefront render /products/trail-runner --strict > page.html
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The HTML goes to stdout, and the errors are in it too, as `Liquid error (sections/x line 12): …`.
|
|
214
|
+
On stderr, each error, then a summary:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
Liquid error (layout/theme line 34): divided by 0
|
|
218
|
+
status 200 · template product · 1 Liquid error · 0 warnings
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
With `--strict`, exit `1` when the page has Liquid errors, or when nothing is at the path
|
|
222
|
+
(`error: nothing at /x: the page is the theme's 404`). `npx local-storefront routes` lists every
|
|
223
|
+
page of the data, and each one renders: loop over it to check them all (`--all` adds the other
|
|
224
|
+
languages, a search and the sections the theme fetches). It leaves out `/account`, a page of
|
|
225
|
+
lsf's, not of the theme.
|
|
226
|
+
|
|
227
|
+
### Run the checks in CI
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
# .github/workflows/storefront.yml
|
|
231
|
+
name: storefront
|
|
232
|
+
on: [push, pull_request]
|
|
233
|
+
jobs:
|
|
234
|
+
storefront:
|
|
235
|
+
runs-on: ubuntu-latest
|
|
236
|
+
steps:
|
|
237
|
+
- uses: actions/checkout@v7
|
|
238
|
+
- uses: actions/setup-node@v7
|
|
239
|
+
with:
|
|
240
|
+
node-version: 24 # 18 or later
|
|
241
|
+
cache: npm
|
|
242
|
+
- run: npm ci
|
|
243
|
+
- run: npx local-storefront validate --format github
|
|
244
|
+
- run: npx local-storefront check --format github
|
|
245
|
+
- name: Render every page, and fail on a Liquid error
|
|
246
|
+
run: |
|
|
247
|
+
npx local-storefront routes --all > routes.txt
|
|
248
|
+
failed=0
|
|
249
|
+
while read -r route; do
|
|
250
|
+
npx local-storefront render "$route" --strict --format github > /dev/null || failed=1
|
|
251
|
+
done < routes.txt
|
|
252
|
+
exit $failed
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`npm ci` needs `package-lock.json`: commit it with `package.json` after the
|
|
256
|
+
`npm install --save-dev local-storefront` of the setup. Commit `shopify-local/` too, `schema/`
|
|
257
|
+
and `README.md` included: the steps read the store data, and editors read `schema/`, which
|
|
258
|
+
`init` writes again at each run. `--format github` shows each problem on its line of the pull
|
|
259
|
+
request. `routes --all` adds the other languages, a search and the sections the theme fetches;
|
|
260
|
+
the step stops at once when `routes` fails (a wrong `--theme`), renders every route, and fails
|
|
261
|
+
at the end if one has a Liquid error. Add
|
|
262
|
+
`- run: npx local-storefront fmt --check` only once the theme is formatted: a stock theme is not.
|
|
263
|
+
With browser tests: [testing](testing.md#in-ci).
|
|
264
|
+
|
|
265
|
+
### Format files
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npx local-storefront fmt --check
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Lists the files that are not formatted; exit `1` if there are any. `npx local-storefront fmt`
|
|
272
|
+
formats them. `npx local-storefront fmt <files>` formats only those.
|
|
273
|
+
|
|
274
|
+
### Find what makes a page slow
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
npx local-storefront profile /collections/all --points
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Points are deterministic: the same at every run and on every machine, so they can be compared
|
|
281
|
+
before and after a change. Times are not. Guide: [performance](performance.md).
|
|
282
|
+
|
|
283
|
+
### Run the Functions of a Shopify app
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
npx local-storefront app function run --app ../my-app --scenario <name> --format json
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Prints the checkout the Functions produce for the cart of a scenario, a file of
|
|
290
|
+
`shopify-local/checkouts/` (`app init` writes a set). Without `--scenario`, it is the cart of the
|
|
291
|
+
store data's `session`, empty in the demo store. The JSON has `lines`,
|
|
292
|
+
`discounts`, `deliveryGroups`, `paymentMethods` and `expressMethods` (with their `placements`),
|
|
293
|
+
`errors`, `cost`, in `runs` the input, the output, the logs and the error of each Function, and in
|
|
294
|
+
`idle` the Functions the shop's checkout left out for want of an enabled owner (a payment
|
|
295
|
+
customization, …), with why. A Function named on the command line runs whether or not the shop
|
|
296
|
+
has an owner of it. Exits with `1` when a Function fails to run, or when a Function named on the
|
|
297
|
+
command line did not run; it says why. Guide: [checkout](checkout.md#owners-and-scenarios).
|
|
298
|
+
|
|
299
|
+
`npx local-storefront app function schema <handle>` writes the Function's `schema.graphql`, from
|
|
300
|
+
lsf's copy of the API's schema, offline.
|
|
301
|
+
|
|
302
|
+
For CI, `npx local-storefront app check --build --format json` builds the extensions, then runs
|
|
303
|
+
the Functions of every scenario and checks the `expect` of each, what its checkout must look like
|
|
304
|
+
([expected results](checkout.md#expected-results)): `ok`, `failures`, `invalid`, `expectations`
|
|
305
|
+
(`checked`, and in `unmet` the `scenario`, the `assertion`, `expected` and `actual` of each
|
|
306
|
+
expectation not met), `notOffered` (an extension the checkout does not offer, and why),
|
|
307
|
+
`stale` (a Function whose files changed after its module was built), `themeExtensions` (what
|
|
308
|
+
Shopify rejects in a theme app extension), each a failure, and `ranInNoScenario`, the Functions
|
|
309
|
+
no scenario ran (a warning: the exit code stays `0`). Assert what a Function decides with an `expect` rather than piping
|
|
310
|
+
`app function run --format json` into `jq`.
|
|
311
|
+
|
|
312
|
+
### Test the backend of an app
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
npx local-storefront app dev --app . --app-url http://localhost:3000 --port 9393 --static --quiet
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The local Admin API, OAuth and the app's webhooks, for its backend, with the app's checkout.
|
|
319
|
+
`serve --app . --only admin` runs the Admin API alone ([what runs](cli.md#what-runs)): a test
|
|
320
|
+
that needs neither the theme nor the checkout. Once `ready:` is printed,
|
|
321
|
+
`GET /__lsf/admin` gives the shop (`defaultShop`), the path of the API (`graphql`) and the token
|
|
322
|
+
(`shops[0].token`). Webhooks go to `--app-url`, signed with the client secret the start lines
|
|
323
|
+
name; without it, those that lead off this machine are `not_sent`.
|
|
324
|
+
`GET /__lsf/webhooks?shop=<shop>&since=<last>&wait=1` waits for one: `last` is in every answer
|
|
325
|
+
of `GET /__lsf/webhooks`, read it before the action. A shop for each test, orders from the
|
|
326
|
+
checkout, resets: [test your backend](admin.md#test-your-backend).
|
|
327
|
+
|
|
328
|
+
### Run a server
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
npx local-storefront serve --port 0 --static --quiet
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
- **Ready.** Run it in the background and read its stderr until a line matches
|
|
335
|
+
`^ready:\s+(http://\S+)`: the URL is group 1. Every line of the server goes to stderr.
|
|
336
|
+
`serving:` names the parts it runs (`theme`, `checkout`, `admin`: [what runs](cli.md#what-runs)).
|
|
337
|
+
With `--app`, the `admin:` lines (the token, the client secret), `webhooks:`, `proxy:`,
|
|
338
|
+
`embedded:` (the app in lsf's admin), `pricing:`, `partner:` and `polaris:` come before
|
|
339
|
+
`ready:`, each when it applies, `home:` and `watch:` after it; read the token from
|
|
340
|
+
`GET /__lsf/admin` rather than from the text. Without `--app`, or with `--only` leaving out
|
|
341
|
+
the admin part, none of them is printed, and the Admin API is not served: its routes answer
|
|
342
|
+
`404` with an `X-LSF-Reason`.
|
|
343
|
+
- **Failed.** It exits with `2` before `ready:` when the port is taken (`error: cannot listen
|
|
344
|
+
on …`), or with `--strict` when the data has errors (on stderr; `validate --format json` gives
|
|
345
|
+
them as JSON).
|
|
346
|
+
- **Stop.** Send `SIGTERM`: it finishes the requests it is answering, for 3 seconds at most,
|
|
347
|
+
and exits with `0`.
|
|
348
|
+
|
|
349
|
+
While it runs:
|
|
350
|
+
|
|
351
|
+
- `GET /__lsf/status` gives the data diagnostics, the templates, the customers and every page.
|
|
352
|
+
- `PUT /__lsf/session` sets the cart, the customer and data for one session; a wrong body
|
|
353
|
+
answers `422` with the same diagnostics as `npx local-storefront validate --format json`.
|
|
354
|
+
- The header `x-lsf-liquid-errors` counts the Liquid errors of a page; it is absent when there
|
|
355
|
+
are none. `x-lsf-not-found` says why a page is a 404.
|
|
356
|
+
|
|
357
|
+
Reference: [control API](control-api.md).
|
|
358
|
+
|
|
359
|
+
## What not to expect
|
|
360
|
+
|
|
361
|
+
local-storefront renders themes, and runs your own app with `--app <dir>`: its checkout, its
|
|
362
|
+
Functions, its UI extensions, the Admin API and webhooks for its backend
|
|
363
|
+
([checkout](checkout.md), [Admin API](admin.md)). It runs no third-party app in the theme, and
|
|
364
|
+
the theme's cart has no discounts, taxes or shipping rates. A difference with Shopify that is
|
|
365
|
+
known is listed in [compatibility](compatibility.md#known-differences): check there before you
|
|
366
|
+
report one.
|