Run it実行する
In the folder of your index.html from step 11:
ステップ11のindex.htmlがあるフォルダで実行します。
$ npx @wcstack/lint index.html 0 error(s), 0 warning(s), 0 info
If you see seven warnings about .stock, .name and .price instead, npx ran a checker older than 4.0.2, which doesn't read the type comments of step 8. Run npx @wcstack/lint@latest index.html.
代わりに.stock・.name・.priceについての警告が7つ出たら、npxが4.0.2より古い検査器を実行しています。その版はステップ8の型のコメントを読みません。npx @wcstack/lint@latest index.htmlを実行してください。
The checker knows every wcs-* tag: the properties each one has, which of them are outputs, inputs and commands, and how <wcs-storage> and a trigger behave at startup. That is what the three mistakes below need.
検査器はすべてのwcs-*タグを知っています。それぞれがどんなプロパティを持ち、そのどれが出力・入力・命令か、<wcs-storage>やtriggerが起動時にどう振る舞うかです。下の3つの間違いには、それが要ります。
Break it on purposeわざと壊してみる
Make each change in your step-11 page, try it in the browser, run the checker, then undo it. The line numbers are those of the step-11 file.
ステップ11のページに変更を1つずつ加え、ブラウザで試し、検査器を実行し、変更を元に戻します。行番号はステップ11のファイルのものです。
1. Bind the cart itself1. カートそのものをバインドする
Change the tag's binding to data-wcs="value: cart". In the browser, add House Blend twice: the page says 2, the stored cart says 1. Reload: the cart is gone. The checker gives both reasons:
タグのバインディングをdata-wcs="value: cart"に変えます。ブラウザでHouse Blendを2回入れると、ページは2、保存されたカートは1です。再読み込みするとカートは消えます。検査器は両方の理由を挙げます。
$ npx @wcstack/lint index.html index.html:107:13 warning wcs/storage-seed-clobber The <wcs-storage> value-bound slot "cart" is seeded with []. The initial write-back overwrites the persisted value on every load — add #init=element to the binding (`value#init=element: cart`) so the stored value wins, and seed the slot with null (what an empty key loads as) index.html:107:20 warning wcs/input-nested-write "cart" is bound to the input "value" of <wcs-storage>, but the page writes below it ("cart.*.quantity"). A binding is re-applied only when "cart" itself is assigned, so those writes never reach <wcs-storage> — bind a getter that reads the fields by path (`this["cart.*.quantity"]`, `$getAll`) and returns a new value, or replace "cart" instead of writing below it 0 error(s), 2 warning(s), 0 info
The first is the reload: the starting [] is saved over the stored cart. The second is the quantity: addToCart and the stepper write cart.*.quantity, below the bound path. Step 11's savedCart answers both.
1つ目は再読み込みの件です。最初の[]が保存済みのカートの上に保存されます。2つ目は数量の件です。addToCartと数量のボタンは、バインドしたパスの下のcart.*.quantityに書きます。ステップ11のsavedCartが、どちらにも答えています。
2. Let the element win, with the cart2. カートのまま要素を勝たせる
Now try data-wcs="value#init=element: cart", in a private window so that localStorage starts empty. “Add to cart” does nothing, and the console says Cannot read properties of null (reading 'findIndex'): the empty key loaded as null, and #init=element wrote it into cart. The checker:
次はdata-wcs="value#init=element: cart"を、localStorageが空で始まるプライベートウィンドウで試します。「Add to cart」は何もせず、コンソールにCannot read properties of null (reading 'findIndex')が出ます。空のキーがnullを読み、#init=elementがそれをcartに書いたのです。検査器の結果は次のとおりです。
$ npx @wcstack/lint index.html index.html:107:13 warning wcs/storage-init-seed The <wcs-storage> value-bound slot "cart" is seeded with [], but #init=element pulls the element's value into it. On a first visit (an empty key) that is null, so the seed is replaced by null — seed it with null and supply the default where you read it (`this.cart ?? …`), or bind an accessor pair whose setter ignores null index.html:107:33 warning wcs/input-nested-write "cart" is bound to the input "value" of <wcs-storage>, but the page writes below it ("cart.*.quantity"). A binding is re-applied only when "cart" itself is assigned, so those writes never reach <wcs-storage> — bind a getter that reads the fields by path (`this["cart.*.quantity"]`, `$getAll`) and returns a new value, or replace "cart" instead of writing below it 0 error(s), 2 warning(s), 0 info
“An accessor pair whose setter ignores null” is savedCart.
「nullを無視するsetterを持つアクセサペア」とは、savedCartのことです。
3. A typo in the type3. 型の打ち間違い
In the @typedef, change name: string to nmae: string. The page is unaffected — the comment is not code — but the checker believes it:
@typedefのname: stringをnmae: stringに変えます。ページには影響しません(コメントはコードではないので)。けれども検査器はそれを信じます。
$ npx @wcstack/lint index.html index.html:137:18 warning wcs/binding-path-missing Path ".name" does not exist in the state definition (expanded: products.*.name) 0 error(s), 1 warning(s), 0 info
The warning points at the product card, and the mistake is in the comment. The type is a promise about the file; when the two disagree, check the file first.
警告は商品カードを指していますが、間違いはコメントの中にあります。型はファイルについての約束です。食い違ったら、まずファイルを確かめましょう。
The checker can't see the file a page fetches, beyond the type you declare, and it can't run the page: a cart that empties on reload for a reason it doesn't know shows up only in the browser. Keep the habit the demos teach: add something, reload, and look. (Forgetting #init=element on savedCart, from step 11, it does report since 4.0.3.)
検査器には、ページが読み込むファイルは宣言した型より先は見えず、ページを動かすこともできません。検査器の知らない理由で再読み込みのたびにカートが空になるなら、それはブラウザでしか分かりません。デモが教える習慣を続けましょう。何かを入れて、再読み込みして、見ることです。(ステップ11の、savedCartの#init=elementの付け忘れは、4.0.3から報告します。)
Part 2 completePart 2 完了
The shop now loads its catalog from a file, says so when that fails, and keeps the cart across reloads and tabs — with two tags and no fetch(), localStorage or try / catch in your code. Along the way:
ショップは商品一覧をファイルから読み、失敗したらそれを伝え、カートを再読み込みやタブをまたいで残すようになりました。使ったのはタグ2つで、コードにはfetch()もlocalStorageもtry / catchもありません。その途中で学んだこと:
- A
wcs-*tag is an element with properties: attributes configure it,data-wcsbinds its outputs (value,loading,error) and inputs (trigger,value). - A getter in front of outside data (
catalog ?? []) keepsnullaway from the rest of the page; a path doesn't care whether it is stored or computed. - Store each fact once: the cart keeps ids and quantities, and row getters look up the rest.
- A binding is re-applied when its own path is assigned. Bind a getter that reads every field by path, and use
#init=elementso the stored value wins at load. - JSDoc
@typedef/@typetell the checker the shape of data it cannot see.
wcs-*タグはプロパティを持つ要素。属性で設定し、data-wcsで出力(value・loading・error)と入力(trigger・value)をバインドする。- 外から来るデータの前に置いたgetter(
catalog ?? [])が、nullをページの残りから遠ざける。パスは保存された値か計算された値かを問わない。 - 1つの事実は1か所に。カートはIDと数量を持ち、残りは行getterで引く。
- バインディングは自分のパスへの代入で当て直される。フィールドをすべてパスで読むgetterをバインドし、
#init=elementで読み込み時に保存値を勝たせる。 - JSDocの
@typedef/@typeが、検査器に見えないデータの形を伝える。
Coming nextこの先
Part 3 — Grow into an app adds product pages with <wcs-router>, splits out a component, tests the page with @wcstack/testing, and publishes it with a pinned, verified version. From there on you need Node.js and a local server.
Part 3 アプリに育てるでは、<wcs-router>で商品ページを加え、部品に分け、@wcstack/testingでテストし、固定して検証できるバージョンで公開します。ここから先はNode.jsとローカルサーバーが必要です。