@wcstack/state 1.28.0 → 1.29.0
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/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.esm.js +174 -17
- package/dist/index.esm.js.map +1 -1
- package/dist/parser.d.ts +42 -1
- package/dist/parser.esm.js +98 -23
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -807,6 +807,11 @@ type DevtoolsEvent = {
|
|
|
807
807
|
readonly maxDepth: number;
|
|
808
808
|
/** 打ち切ったバッチに載っていたアドレスのパス(報告用) */
|
|
809
809
|
readonly paths: readonly string[];
|
|
810
|
+
} | {
|
|
811
|
+
readonly type: "state:watch-fired";
|
|
812
|
+
readonly stateName: string;
|
|
813
|
+
/** `$watch` の宣言キー(ワイルドカードを含む生のパス) */
|
|
814
|
+
readonly path: string;
|
|
810
815
|
} | {
|
|
811
816
|
readonly type: "propagation:suppressed";
|
|
812
817
|
readonly reason: "confirmation" | "visited-edge";
|
package/dist/index.esm.js
CHANGED
|
@@ -665,13 +665,6 @@ function setBindingsByNode(node, bindings) {
|
|
|
665
665
|
bindingsByNode.set(node, bindings);
|
|
666
666
|
}
|
|
667
667
|
|
|
668
|
-
const STRUCTURAL_BINDING_TYPE_SET = new Set([
|
|
669
|
-
"if",
|
|
670
|
-
"elseif",
|
|
671
|
-
"else",
|
|
672
|
-
"for",
|
|
673
|
-
]);
|
|
674
|
-
|
|
675
668
|
/**
|
|
676
669
|
* errorGuidance.ts — エラーメッセージへの self-fix 誘導(GTM 2-5 /
|
|
677
670
|
* docs/static-wiring-dx-design.md §3)。
|
|
@@ -735,12 +728,22 @@ function didYouMean(input, candidates) {
|
|
|
735
728
|
/**
|
|
736
729
|
* lint への誘導(誘導付きメッセージ共通の一文)。
|
|
737
730
|
* **lint が実際にそのケースを検出するサイトにだけ付ける** — 検出しないケースに
|
|
738
|
-
* 付けると「エラー → lint 実行 → clean
|
|
739
|
-
*
|
|
740
|
-
*
|
|
731
|
+
* 付けると「エラー → lint 実行 → clean」の空振りで検証ループの信頼を毀損する。
|
|
732
|
+
* 現在 lint 未検出のため付けないもの: DCC 宣言・watch の空キー / Object.prototype
|
|
733
|
+
* 継承名 / ワイルドカード深度超過。
|
|
734
|
+
* なお hint 付きサイト内でも被覆は部分的でありうる(例: `$watch: ident` の実体が
|
|
735
|
+
* 非オブジェクトだった場合、ランタイムは評価後の値で raise するが lint は宣言 shape
|
|
736
|
+
* から断定できず沈黙する)。サイト粒度の hint ではこの残余は構造的に避けられない。
|
|
741
737
|
*/
|
|
742
738
|
const LINT_HINT = " Validate statically: npx @wcstack/lint <file>.";
|
|
743
739
|
|
|
740
|
+
const STRUCTURAL_BINDING_TYPE_SET = new Set([
|
|
741
|
+
"if",
|
|
742
|
+
"elseif",
|
|
743
|
+
"else",
|
|
744
|
+
"for",
|
|
745
|
+
]);
|
|
746
|
+
|
|
744
747
|
/**
|
|
745
748
|
* errorMessages.ts
|
|
746
749
|
*
|
|
@@ -1959,9 +1962,9 @@ function parseBindTextsForElement(bindText) {
|
|
|
1959
1962
|
if (results.length > 1) {
|
|
1960
1963
|
const isIncludeSingleBinding = results.some(r => STRUCTURAL_BINDING_TYPE_SET.has(r.bindingType));
|
|
1961
1964
|
if (isIncludeSingleBinding) {
|
|
1962
|
-
//
|
|
1963
|
-
//
|
|
1964
|
-
raiseError(`[wcs/template-syntax] Invalid bindText: "${bindText}". 'if', 'elseif', 'else', and 'for' bindings must be single binding. Put the structural binding alone in its own data-wcs (e.g. <template data-wcs="for: items">)
|
|
1965
|
+
// lint 側の単独バインディング検査(bindingValidator の structuralMustBeSingle)が
|
|
1966
|
+
// 同じケースを検出するため誘導を付ける(三面同語彙)。
|
|
1967
|
+
raiseError(`[wcs/template-syntax] Invalid bindText: "${bindText}". 'if', 'elseif', 'else', and 'for' bindings must be single binding. Put the structural binding alone in its own data-wcs (e.g. <template data-wcs="for: items">).${LINT_HINT}`);
|
|
1965
1968
|
}
|
|
1966
1969
|
}
|
|
1967
1970
|
return results;
|
|
@@ -8145,7 +8148,7 @@ async function buildBindings(root) {
|
|
|
8145
8148
|
}
|
|
8146
8149
|
}
|
|
8147
8150
|
|
|
8148
|
-
var version = "1.
|
|
8151
|
+
var version = "1.29.0";
|
|
8149
8152
|
var pkg = {
|
|
8150
8153
|
version: version};
|
|
8151
8154
|
|
|
@@ -9181,6 +9184,148 @@ function getUpdater() {
|
|
|
9181
9184
|
return updater;
|
|
9182
9185
|
}
|
|
9183
9186
|
|
|
9187
|
+
/**
|
|
9188
|
+
* devtools/declaredBindings.ts — 宣言レベルのバインディング列挙
|
|
9189
|
+
* (IDevtoolsSource.getDeclaredBindings の実装・protocol v1 追補)。
|
|
9190
|
+
*
|
|
9191
|
+
* devtools 側の DOM 再スキャン(declaredScan — 「正本 bindTextParser に非追随」と
|
|
9192
|
+
* 自己申告する簡易パーサ)を置き換える正本実装。ランタイム自身のパーサと
|
|
9193
|
+
* fragment レジストリで答えるため:
|
|
9194
|
+
* - パース結果は実行時解釈と**同一**(複製パーサのドリフトが構造的に消える)
|
|
9195
|
+
* - DOM から引き上げられた構造テンプレート内部(for/if の中身)も列挙できる
|
|
9196
|
+
* (DOM 再スキャンでは原理的に不可視だった領域)
|
|
9197
|
+
*
|
|
9198
|
+
* **戻り値は「宣言の集合」であり、インスタンスの multiset ではない**:
|
|
9199
|
+
* レンダリング済みページでは行クローンが `data-wcs` 属性とネストアンカー
|
|
9200
|
+
* (同一 UUID)を保持したまま live DOM に入るため、素朴な走査は宣言 1 件を
|
|
9201
|
+
* 行数分だけ重複列挙してしまう。ここでは宣言タプル(stateName / path / prop /
|
|
9202
|
+
* bindingType / フィルタ列)で dedupe し、宣言 1 件 = エントリ 1 件を保証する。
|
|
9203
|
+
* インスタンス粒度(どの行のどのノードか)は live の binding 台帳
|
|
9204
|
+
* (state:binding-added)の守備範囲で、こちらには持たせない。
|
|
9205
|
+
* 同一タプルの独立した静的宣言(同じバインディングを書いた別要素)も 1 件に
|
|
9206
|
+
* 畳まれる — node は最初に見つかったものを代表として持つ。
|
|
9207
|
+
*
|
|
9208
|
+
* fragment(構造テンプレート内部)は **rootNode 配下の live アンカーから到達可能な
|
|
9209
|
+
* UUID の推移閉包**として列挙する。ページ大域のレジストリを直接舐めないため、
|
|
9210
|
+
* 別 root の fragment や破棄済みビューの stale fragment は載らない。
|
|
9211
|
+
*
|
|
9212
|
+
* spread(`...:`)は attribute 起点では live Element の wcBindable から展開する
|
|
9213
|
+
* (設計 §5-1)。対象カスタム要素が未定義なら既存契約どおり spread のまま残す。
|
|
9214
|
+
*
|
|
9215
|
+
* **既知の盲点(時系列依存)**: 構造テンプレート外のテキストバインディングは、
|
|
9216
|
+
* binding start がコメントアンカーを空 Text に差し替えるため、**活性化後の DOM
|
|
9217
|
+
* から消えて列挙に現れない**(mustache は変換前も素の Text で不可視)。早期
|
|
9218
|
+
* アタッチ時は live 台帳(binding-added)が同領域を持つ。遅延アタッチでは
|
|
9219
|
+
* declared / live のどちらからも見えない — protocol §6 の既知非対称の一部。
|
|
9220
|
+
*
|
|
9221
|
+
* エラーパス以外もフック接続時にしか呼ばれない pull API であり、ホットパスには
|
|
9222
|
+
* 乗らない。壊れた宣言(不正 bindText)はエントリを捨てて先へ進む(検査は lint の
|
|
9223
|
+
* 責務 — ここは観測面)。
|
|
9224
|
+
*/
|
|
9225
|
+
// bindings/parseCommentNode.ts の EMBEDDED_REGEX と同形(keyword を自前で保持する
|
|
9226
|
+
// ために複製する — parseCommentNode は keyword を捨てて値しか返さないため、
|
|
9227
|
+
// 「未登録の構造アンカー(SSR pre-hydration ウィンドウ等)」と「埋め込みテキスト」
|
|
9228
|
+
// を区別できず、UUID をパスとして誤パースした bogus エントリが生まれる)。
|
|
9229
|
+
const COMMENT_PATTERN = /^\s*@@\s*(.*?)\s*:\s*(.+?)\s*$/;
|
|
9230
|
+
function declarationKey(info) {
|
|
9231
|
+
const filters = [...info.inFilters, ...info.outFilters]
|
|
9232
|
+
.map((f) => `${f.filterName}(${f.args.join(",")})`)
|
|
9233
|
+
.join("|");
|
|
9234
|
+
return [info.stateName, info.statePathName, info.propName, info.bindingType, filters].join("\u0000");
|
|
9235
|
+
}
|
|
9236
|
+
function toInfo(node, parsed, origin, raw) {
|
|
9237
|
+
return {
|
|
9238
|
+
node,
|
|
9239
|
+
propName: parsed.propName,
|
|
9240
|
+
statePathName: parsed.statePathName,
|
|
9241
|
+
stateName: parsed.stateName,
|
|
9242
|
+
bindingType: parsed.bindingType,
|
|
9243
|
+
inFilters: parsed.inFilters,
|
|
9244
|
+
outFilters: parsed.outFilters,
|
|
9245
|
+
origin,
|
|
9246
|
+
raw,
|
|
9247
|
+
};
|
|
9248
|
+
}
|
|
9249
|
+
/**
|
|
9250
|
+
* rootNode 配下の live DOM と、そこから到達可能な fragment から宣言集合を列挙する。
|
|
9251
|
+
* ヘッダの規定(宣言集合 / 到達閉包 / spread 展開 / テキストの盲点)を参照。
|
|
9252
|
+
*/
|
|
9253
|
+
function collectDeclaredBindings(rootNode) {
|
|
9254
|
+
const out = [];
|
|
9255
|
+
const seenKeys = new Set();
|
|
9256
|
+
const visitedFragments = new Set();
|
|
9257
|
+
const push = (info) => {
|
|
9258
|
+
const key = declarationKey(info);
|
|
9259
|
+
if (seenKeys.has(key))
|
|
9260
|
+
return;
|
|
9261
|
+
seenKeys.add(key);
|
|
9262
|
+
out.push(info);
|
|
9263
|
+
};
|
|
9264
|
+
/** fragment の内部宣言を emit し、ネストした構造アンカーへ再帰する(到達閉包)。 */
|
|
9265
|
+
const visitFragment = (uuid) => {
|
|
9266
|
+
if (visitedFragments.has(uuid))
|
|
9267
|
+
return;
|
|
9268
|
+
visitedFragments.add(uuid);
|
|
9269
|
+
const fragmentInfo = getFragmentInfoByUUID(uuid);
|
|
9270
|
+
if (fragmentInfo === null)
|
|
9271
|
+
return;
|
|
9272
|
+
for (const nodeInfo of fragmentInfo.nodeInfos) {
|
|
9273
|
+
for (const parsed of nodeInfo.parseBindTextResults) {
|
|
9274
|
+
push(toInfo(null, parsed, "fragment", ""));
|
|
9275
|
+
if (parsed.uuid != null) {
|
|
9276
|
+
visitFragment(parsed.uuid);
|
|
9277
|
+
}
|
|
9278
|
+
}
|
|
9279
|
+
}
|
|
9280
|
+
};
|
|
9281
|
+
const doc = rootNode.ownerDocument ?? rootNode;
|
|
9282
|
+
const walker = doc.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT);
|
|
9283
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
9284
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
9285
|
+
const element = node;
|
|
9286
|
+
const bindText = element.getAttribute(config.bindAttributeName);
|
|
9287
|
+
if (bindText === null || bindText.length === 0)
|
|
9288
|
+
continue;
|
|
9289
|
+
try {
|
|
9290
|
+
// spread は live Element の wcBindable から展開(未定義要素は spread のまま = 既存契約)
|
|
9291
|
+
const results = expandSpread(element, parseBindTextsForElement(bindText));
|
|
9292
|
+
for (const parsed of results) {
|
|
9293
|
+
push(toInfo(element, parsed, "attribute", bindText));
|
|
9294
|
+
}
|
|
9295
|
+
}
|
|
9296
|
+
catch {
|
|
9297
|
+
// 不正な宣言は観測面では捨てる(検査は lint / ランタイム raiseError の責務)
|
|
9298
|
+
}
|
|
9299
|
+
continue;
|
|
9300
|
+
}
|
|
9301
|
+
// コメントアンカー: 構造ディレクティブ(UUID → fragment 登録済み)または
|
|
9302
|
+
// 埋め込みテキストバインディング。
|
|
9303
|
+
const match = COMMENT_PATTERN.exec(node.data.trim());
|
|
9304
|
+
if (match === null)
|
|
9305
|
+
continue;
|
|
9306
|
+
const keyword = match[1] || config.commentTextPrefix;
|
|
9307
|
+
const value = match[2];
|
|
9308
|
+
const fragmentInfo = getFragmentInfoByUUID(value);
|
|
9309
|
+
if (fragmentInfo !== null) {
|
|
9310
|
+
push(toInfo(node, fragmentInfo.parseBindTextResult, "comment", value));
|
|
9311
|
+
visitFragment(value);
|
|
9312
|
+
continue;
|
|
9313
|
+
}
|
|
9314
|
+
if (keyword !== config.commentTextPrefix) {
|
|
9315
|
+
// 構造アンカーなのに fragment 未登録(SSR の pre-hydration ウィンドウ等)、
|
|
9316
|
+
// または未知 keyword — UUID や不明値をパスとして誤パースしない。
|
|
9317
|
+
continue;
|
|
9318
|
+
}
|
|
9319
|
+
try {
|
|
9320
|
+
push(toInfo(node, parseBindTextForEmbeddedNode(value), "comment", value));
|
|
9321
|
+
}
|
|
9322
|
+
catch {
|
|
9323
|
+
// 同上
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
return out;
|
|
9327
|
+
}
|
|
9328
|
+
|
|
9184
9329
|
/**
|
|
9185
9330
|
* devtools/types.ts
|
|
9186
9331
|
*
|
|
@@ -9322,6 +9467,9 @@ function createStateElementSummary(element) {
|
|
|
9322
9467
|
eventTokenNames: element.eventTokenNames,
|
|
9323
9468
|
staticDependency: element.staticDependency,
|
|
9324
9469
|
dynamicDependency: element.dynamicDependency,
|
|
9470
|
+
// protocol v1 追補(additive)— 配線カバレッジの宣言面(設計 §4)。
|
|
9471
|
+
// 旧 IStateElement 実装(optional)では undefined になりうるため null に畳む。
|
|
9472
|
+
watchPaths: element.watchPaths ?? null,
|
|
9325
9473
|
};
|
|
9326
9474
|
}
|
|
9327
9475
|
function requireStateElement(name, rootNode) {
|
|
@@ -9403,6 +9551,9 @@ function registerDevtoolsSource() {
|
|
|
9403
9551
|
}
|
|
9404
9552
|
});
|
|
9405
9553
|
},
|
|
9554
|
+
getDeclaredBindings(rootNode) {
|
|
9555
|
+
return collectDeclaredBindings(rootNode);
|
|
9556
|
+
},
|
|
9406
9557
|
_setSink: setSink,
|
|
9407
9558
|
};
|
|
9408
9559
|
registeredSource = source;
|
|
@@ -10783,9 +10934,10 @@ function processWatchDeclaration(stateElement, state) {
|
|
|
10783
10934
|
return null;
|
|
10784
10935
|
}
|
|
10785
10936
|
if (typeof declared !== "object" || declared === null) {
|
|
10786
|
-
//
|
|
10787
|
-
//
|
|
10788
|
-
|
|
10937
|
+
// lint 側の findNonObjectWatch が「断定できる形」を同 code で検出するため誘導を付ける。
|
|
10938
|
+
// (LINT_HINT を付けない残りは、lint が検出しない空キー・Object.prototype 継承名・
|
|
10939
|
+
// ワイルドカード深度超過の 3 shape。)
|
|
10940
|
+
raiseError(`[wcs/watch-declaration-invalid] ${STATE_WATCH_NAME} must be an object mapping state paths to handler functions.${LINT_HINT}`);
|
|
10789
10941
|
}
|
|
10790
10942
|
const entries = new Map();
|
|
10791
10943
|
const paths = new Set();
|
|
@@ -11155,6 +11307,11 @@ function fireOne(hit) {
|
|
|
11155
11307
|
// handler 呼び出しより前に更新する
|
|
11156
11308
|
setComputedSnapshot(stateElement, absAddress, cur);
|
|
11157
11309
|
}
|
|
11310
|
+
// 正常発火の観測(設計書 §11 の予約イベント・配線カバレッジの実測面)。
|
|
11311
|
+
// 値は載せない。イベント生成は sink 接続時のみ(ゼロコスト契約)。
|
|
11312
|
+
if (devtoolsSink !== null) {
|
|
11313
|
+
devtoolsSink({ type: "state:watch-fired", stateName: stateElement.name, path: entry.path });
|
|
11314
|
+
}
|
|
11158
11315
|
entry.handler.call(state, cur, prev, ...indexes);
|
|
11159
11316
|
});
|
|
11160
11317
|
}
|