Skip to content

refactor(parser): ParseContextのper-scope fieldをscopegroupに集約#45

Merged
r74tech merged 2 commits into
developfrom
refactor/parsecontext-group-internal
Jun 4, 2026
Merged

refactor(parser): ParseContextのper-scope fieldをscopegroupに集約#45
r74tech merged 2 commits into
developfrom
refactor/parsecontext-group-internal

Conversation

@r74tech

@r74tech r74tech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • ParseContext の per-scope semantics を持つ 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 は破棄される)。

背景

ParseContext は用途の異なる field がフラットに並んでおり、特に primitive 型 (footnoteBlockParsed: boolean) は spread copy で per-scope semantics になっているのに、名前から見ると「global per-document flag」のように見えた。同様のリスクが他フィールドにも潜在しており、型レベルで semantics を明示するのが本リファクタの目的。

Changes

1. 型定義 (packages/parser/src/parser/rules/types.ts)

  • ScopeContext interface 追加。全 field readonly:
    • readonly blockCloseCondition?: (ctx: ParseContext) => boolean
    • readonly excludedBlockNames?: ReadonlySet<string>
    • readonly divClosesBudget?: number
    • readonly footnoteBlockParsed: boolean
  • ParseContext から上記 4 field を削除し、scope: ScopeContext を追加。
  • pos は per-scope cursor だが頻出のため top-level に例外的に残置 (理由を JSDoc に明記)。
  • accumulator (footnotes / tocEntries / codeBlocks / htmlBlocks / bibcites / diagnostics) は top-level のまま (配列参照 spread 共有でそのまま機能)。

2. 利用側

  • packages/parser/src/parser/parse.ts:
    • constructor で scope: { footnoteBlockParsed: false } を初期化。
  • packages/parser/src/parser/rules/block/div.ts:
    • ctx.divClosesBudgetctx.scope.divClosesBudget に。
    • spread を { ...ctx, pos, scope: { ...ctx.scope, divClosesBudget: bodyBudget } } に。
  • packages/parser/src/parser/rules/block/footnoteblock.ts:
    • 判定を ctx.scope.footnoteBlockParsed に。
    • mutation を ctx.scope = { ...ctx.scope, footnoteBlockParsed: true } の immutable replace に。readonly のため nested ctx.scope.X = ... は TS error。
  • packages/parser/src/parser/rules/block/utils.ts:
    • parseBlocksUntil の spread で blockCloseCondition / excludedBlockNamesscope 経由に。
  • 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)

  • collectByElement helper で AST を再帰走査。
  • 単一の [[footnoteblock]] がちょうど 1 個の footnote-block を生む。
  • 二回目の top-level [[footnoteblock]] は text として扱われる (top-level mutation sticks)。
  • [[div]] 内の [[footnoteblock]] + top-level [[footnoteblock]]footnote-block が 2 個出る (per-scope 非伝播の確認)。
  • balanced nested [[div]] の内側 div が container として残る (scope.divClosesBudget 経由)。
  • unbalanced excess [[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 件)。
  • lint / format / typecheck 通過。
  • per-scope semantics の回帰テスト (top-level mutation sticks / nested non-leak / div budget 成功・text 化区別) 追加。
  • rg "ctx\.scope\..*= " で nested mutation の writebacks が残っていないことを確認 (immutable replace 以外の書き込みなし)。

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

r74tech added 2 commits June 4, 2026 20:31
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
r74tech force-pushed the refactor/parsecontext-group-internal branch from 3ccee91 to dad897c Compare June 4, 2026 11:32
@r74tech
r74tech merged commit 77a6192 into develop Jun 4, 2026
9 checks passed
@r74tech
r74tech deleted the refactor/parsecontext-group-internal branch June 4, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant