refactor(parser): ParseContextのper-scope fieldをscopegroupに集約#45
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
wdpr-demo-v1 | dad897c | Commit Preview URL Branch Preview URL |
Jun 04 2026, 11:33 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
decompiler-preview | dad897c | Commit Preview URL Branch Preview URL |
Jun 04 2026, 11:33 AM |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
wdpr-demo-v1-files | dad897c | Commit Preview URL Branch Preview URL |
Jun 04 2026, 11:33 AM |
issue: critical-arch-ParseContext-internal-fields-grouping.md
ParseContext から per-scope semantics を持つ field 4つ
(footnoteBlockParsed / divClosesBudget / blockCloseCondition /
excludedBlockNames) を新しい ScopeContext interface に集約。
ScopeContext の field は全て readonly。spread で参照共有されるオブジェクト
だが、mutation は禁止して `ctx.scope = { ...ctx.scope, X: ... }` の
immutable replace で行う。これにより:
- 型レベルで「これは per-scope semantics」が明示される
- TS が nested mutation (ctx.scope.X = ...) を error にする
- 投機 parse rollback safe (失敗した child context の scope は破棄される)
変更ファイル:
- packages/parser/src/parser/rules/types.ts: ScopeContext 追加、ParseContext から移動
- packages/parser/src/parser/parse.ts: constructor で scope 初期化
- packages/parser/src/parser/rules/block/div.ts: divClosesBudget 参照を scope 経由に
- packages/parser/src/parser/rules/block/footnoteblock.ts: footnoteBlockParsed mutation を scope 全体 immutable replace に
- packages/parser/src/parser/rules/block/utils.ts: spread サイトを scope 経由に
- packages/parser/src/parser/rules/inline/utils.ts: blockCloseCondition / excludedBlockNames / footnoteBlockParsed 参照を scope 経由に
- packages/parser/src/parser/rules/index.ts: ScopeContext を type export
- tests/unit/parser/scope.test.ts: per-scope semantics の回帰テスト追加
BREAKING CHANGE: 外部で ParseContext 互換オブジェクトを手書きしている
消費者は scope: { footnoteBlockParsed: false, ... } の追加が必要。
ParseContext は内部公開型 (rules/types.ts) で、通常の parse() / Parser
class 経由なら影響なし。
scope のリファクタが現状の挙動を保つことを直接検証するためテストを追加: - collectByElement helper で AST を再帰走査 - [[div]] 内の [[footnoteblock]] + top-level [[footnoteblock]] で footnote-block が 2 個出ることを確認 (per-scope 非伝播) - div container を再帰走査し type が "div"/"div_" のものだけを数えることで 「成功 / text 化」を区別 refactor 自体の挙動変更はなし、テストカバレッジの強化のみ。
r74tech
force-pushed
the
refactor/parsecontext-group-internal
branch
from
June 4, 2026 11:32
3ccee91 to
dad897c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ParseContextの per-scope semantics を持つ 4 フィールド (footnoteBlockParsed/divClosesBudget/blockCloseCondition/excludedBlockNames) を新しいScopeContextinterface に集約。ScopeContextの field は全てreadonly。spread で参照共有されるオブジェクトだが mutation は禁止し、ctx.scope = { ...ctx.scope, X: ... }の immutable replace で行う。ctx.scope.X = ...) を error にする。投機 parse rollback safe (失敗した child context の scope は破棄される)。背景
ParseContextは用途の異なる field がフラットに並んでおり、特に primitive 型 (footnoteBlockParsed: boolean) は spread copy で per-scope semantics になっているのに、名前から見ると「global per-document flag」のように見えた。同様のリスクが他フィールドにも潜在しており、型レベルで semantics を明示するのが本リファクタの目的。Changes
1. 型定義 (
packages/parser/src/parser/rules/types.ts)ScopeContextinterface 追加。全 fieldreadonly:readonly blockCloseCondition?: (ctx: ParseContext) => booleanreadonly excludedBlockNames?: ReadonlySet<string>readonly divClosesBudget?: numberreadonly footnoteBlockParsed: booleanParseContextから上記 4 field を削除し、scope: ScopeContextを追加。posは per-scope cursor だが頻出のため top-level に例外的に残置 (理由を JSDoc に明記)。footnotes/tocEntries/codeBlocks/htmlBlocks/bibcites/diagnostics) は top-level のまま (配列参照 spread 共有でそのまま機能)。2. 利用側
packages/parser/src/parser/parse.ts:scope: { footnoteBlockParsed: false }を初期化。packages/parser/src/parser/rules/block/div.ts:ctx.divClosesBudgetをctx.scope.divClosesBudgetに。{ ...ctx, pos, scope: { ...ctx.scope, divClosesBudget: bodyBudget } }に。packages/parser/src/parser/rules/block/footnoteblock.ts:ctx.scope.footnoteBlockParsedに。ctx.scope = { ...ctx.scope, footnoteBlockParsed: true }の immutable replace に。readonlyのため nestedctx.scope.X = ...は TS error。packages/parser/src/parser/rules/block/utils.ts:parseBlocksUntilの spread でblockCloseCondition/excludedBlockNamesをscope経由に。packages/parser/src/parser/rules/inline/utils.ts:excludedBlockNames/blockCloseCondition/footnoteBlockParsedの参照をctx.scope.*に。packages/parser/src/parser/rules/index.ts:ScopeContextを internal type export に追加。3. テスト (
tests/unit/parser/scope.test.ts)collectByElementhelper で AST を再帰走査。[[footnoteblock]]がちょうど 1 個のfootnote-blockを生む。[[footnoteblock]]は text として扱われる (top-level mutation sticks)。[[div]]内の[[footnoteblock]]+ top-level[[footnoteblock]]でfootnote-blockが 2 個出る (per-scope 非伝播の確認)。[[div]]の内側 div が container として残る (scope.divClosesBudget経由)。[[div]]opens が text 化される (budget enforcement)。BREAKING CHANGE
ParseContextに必須プロパティscope: ScopeContextを追加し、footnoteBlockParsed/divClosesBudget/blockCloseCondition/excludedBlockNamesを top-level から削除。ParseContext互換オブジェクトを手書きしている消費者はscope: { footnoteBlockParsed: false, ... }の追加が必要。ParseContextは内部公開型 (rules/types.ts、@wdprlib/parserの internal index 経由) で、通常のparse()/Parserクラス経由なら影響なし。Test plan
bun testで 1178 件)。rg "ctx\.scope\..*= "で nested mutation の writebacks が残っていないことを確認 (immutable replace 以外の書き込みなし)。