Skip to content

feat(ast, parser, render)!: WikitextSettings.allowHtmlBlocksを追加し[[html]]ブロックを context 別に無効化可能に#46

Merged
r74tech merged 8 commits into
developfrom
feat/settings-allow-html-blocks
Jun 4, 2026
Merged

feat(ast, parser, render)!: WikitextSettings.allowHtmlBlocksを追加し[[html]]ブロックを context 別に無効化可能に#46
r74tech merged 8 commits into
developfrom
feat/settings-allow-html-blocks

Conversation

@r74tech

@r74tech r74tech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • WikitextSettings必須プロパティ allowHtmlBlocks: boolean を追加し、parser と renderer の両側で [[html]] ブロックを無効化できるようにする。
  • createSettings(mode) の既定値: page=true (現状互換) / draft, forum-post, direct-message = false
  • disabled 時は [[html]]...[[/html]] のブロック全体を消費して空要素を返す (中身が text として漏れない)。
  • block 位置だけでなく mid-paragraph (inline) 位置の [[html]] も新規 inline rule で gate。
  • render 側にも renderHtmlBlock 先頭 (counter / resolver より前) に gate を入れ、手組み AST / キャッシュ AST / 別 parser 由来 AST でも enforcement boundary が効くようにする。

背景

[[html]] は raw HTML を iframe 経由で配信する仕組みだが、静的ホスティングや未信頼入力のプレビュー等、その仕組みが使えない / 使いたくない context が存在する。これまでユーザー側で parse 後 AST を走査して html ノードを除去する暫定対処が必要だったが、本フラグ導入により settings 経由でクリーンに無効化できる。

なお Wikidot legacy (lib/Text_Wiki/Text/Wiki.php$disable = ['Html']) では Html は 既定 disable だが、wp は既存ユーザー保護のため page=true を当面維持する。将来的に Wikidot 互換へ寄せる際は別 issue で扱う。

Changes

1. @wdprlib/ast

  • WikitextSettingsallowHtmlBlocks: boolean を必須追加。
  • createSettings(mode) の defaults を上記の通り設定。
  • WikitextMode の JSDoc table を 5 列に拡張。

2. @wdprlib/parser

  • htmlBlockRule に gate を追加:
    • opener の BLOCK_CLOSE を検証した直後で ctx.settings.allowHtmlBlocks === false を判定。
    • close は BLOCK_END_OPEN + name(html) + 任意の whitespace + BLOCK_CLOSE を検証 (malformed [[/html で誤認識して後続テキストが漏れる挙動を防止)。close 末尾の whitespace は consume 側でも skip。
    • disabled かつ close 不在 (truly unclosed) 時は forward lookahead で close 存在を確認し、ない場合のみ NEWLINE+NEWLINE で停止 (後続 paragraph の保護)。
    • disabled の consumed 経路は success: true / elements: [] を返し、html-block-disabled (info) を emit。未閉じ時は unclosed-block (warning) も併用。
  • 新規 htmlInlineRule (packages/parser/src/parser/rules/inline/html.ts):
    • allowHtmlBlocks !== false なら success: false (従来挙動維持)。
    • === false なら [[html]]...[[/html]] を inline で消費し空要素返す。
    • 同じ close-detection と blank-line stop ロジックを共有。
  • inline rules 配列に htmlInlineRule を登録 (commentRule 直後)。

3. @wdprlib/render

  • renderHtmlBlock(ctx, data) の先頭で ctx.settings.allowHtmlBlocks === false 時に早期 return。nextHtmlBlockIndex() counter advance や htmlBlockUrl resolver 呼び出しより前に置くことで、手組み / キャッシュ / 別 parser 由来 AST でも iframe 出力を確実に抑止。

4. テスト

  • tests/unit/parser/settings.test.ts:
    • createSettings の既定値確認 (page=true / 他=false)。
    • enabled (page): [[html]] が html element になり html-blocks に push される / html-block-disabled diagnostic は出ない。
    • disabled (draft): element 出ず html-blocks 空 / html-block-disabled 出る / 本文に漏れない。
    • disabled + unclosed: 最後まで consume / html-block-disabled + unclosed-block 両方 emit。
    • disabled + 周囲非干渉: 前後の paragraph は残る。
    • forum-post / direct-message でも disabled。
    • mid-paragraph [[html]] の inline 漏れ防止。
    • malformed close ([[/html BLOCK_CLOSE 欠落) でも leak しない。
    • Parser クラス直接使用でも gate される。
    • 空行入り valid [[html]] block (page) が正常 parse。
    • 空行入り closed [[html]] block (draft) が leak せず完全消費。
    • whitespace 付き close tag ([[/html ]]) が正しく consume。
    • [[code]] 内の [[html]] は raw 扱いで影響なし。
    • unclosed disabled で blank-line stop → 後続 paragraph 保持。
  • tests/unit/render/settings.test.ts:
    • enabled (page): iframe 出力。
    • disabled: 手組み AST に html element があっても iframe 出ない。
    • disabled: htmlBlockUrl resolver が呼ばれない。

BREAKING CHANGE

  • WikitextSettings に必須プロパティ allowHtmlBlocks が追加されました。手書きで WikitextSettings を構築しているユーザーは allowHtmlBlocks フィールド追加が必要 (createSettings(mode) を経由していれば影響なし)。
  • WikitextSettings.allowHtmlBlocks === false の context では [[html]] ブロックが AST にも html-blocks 配列にも出ず、render 側も iframe を出さない。

Test plan

  • 全体テスト通過 (bun test で 1190 件)。
  • lint / format / typecheck 通過。
  • 既存 fixture (tests/fixtures/html/basic, tests/fixtures/html/fail) で page mode の従来挙動を回帰確認。
  • disabled 時に body が text として漏れないことを block / inline / unclosed / malformed-close / 空行入り / 周囲非干渉 / Parser 直接使用 / render gate などのケースで確認。

r74tech added 8 commits June 1, 2026 21:15
createSettings の各モード既定値:
- page: true (現状互換)
- draft / forum-post / direct-message: false

Wikidot legacy では Html rule を $disable に入れているため (Wiki.php
line 145-147)、本来 page も false が Wikidot 互換だが、wp は transition
のため page=true を維持する。

BREAKING CHANGE: WikitextSettings に必須プロパティ allowHtmlBlocks が
追加された。手書きで WikitextSettings を構築している消費者は
allowHtmlBlocks フィールドの追加が必要。createSettings(mode) を経由
していれば影響なし。
opener の `]]` を検証してから settings.allowHtmlBlocks === false を判定。
disabled 時はブロック全体を消費して空要素 (success:true / elements:[]) を
返し、ctx.htmlBlocks には push しない。close が見つからない場合も EOF まで
消費して同じく空要素を返す (unclosed-block warning + html-block-disabled
info の両方を emit)。中身が text として漏れない。

malformed opener (closing `]]` 欠落) は従来どおり success:false で
fallback する。enabled 時の挙動は完全維持。

BREAKING CHANGE: WikitextSettings.allowHtmlBlocks が false の context
では [[html]] が AST にも html-blocks 配列にも出ない。
renderHtmlBlock の先頭で ctx.settings.allowHtmlBlocks === false を判定し
早期 return。nextHtmlBlockIndex() の counter advance や htmlBlockUrl
resolver 呼び出しより前に置くことで、disabled-but-still-in-AST な
ブロック (手組み AST、キャッシュ AST、別 parser 由来) でも何も出力しない。

これによりパース側で settings ゲートが効かないパス (例: 手組み AST の
直接 render) でも enforcement boundary が保てる。

BREAKING CHANGE: WikitextSettings.allowHtmlBlocks が false の context
では html element をレンダしない (空)。
parser (tests/unit/parser/settings.test.ts):
- createSettings 既定値 (page=true / draft, forum-post, direct-message=false)
- enabled (page): [[html]] が html element になり html-blocks に push される
- disabled (draft): element 出ず html-blocks 空、html-block-disabled diagnostic、本文に漏れない
- disabled + unclosed: EOF まで consume、unclosed-block + html-block-disabled 両方 emit、漏れない
- disabled + 周囲非干渉: 前後の paragraph は残る
- forum-post / direct-message でも disabled になる

render (tests/unit/render/settings.test.ts):
- enabled: iframe が出る
- disabled: 手組み AST に html element があっても iframe 出ない (enforcement boundary)
- disabled: htmlBlockUrl resolver が呼ばれない
3 件の漏れ・誤検出を修正:

1. (High) close-detection の修正
   parseBlockName で名前が "html" と一致した時点で foundClose=true に
   していたため、malformed `[[/html no-close` (BLOCK_CLOSE 欠落) で close
   と誤認識し、後続テキストが leak する可能性があった。BLOCK_CLOSE まで
   reach できることを確認してから close と認める実装に変更 (whitespace は
   許容)。これにより disabled 時の unclosed パスも常に EOF まで consume
   する。

2. (Medium) text-level の [[html]] strip pre-pass を追加
   block-rule の gate だけでは段落内 `before [[html]]X[[/html]] after`
   のような inline-position occurrence を捕まえられない (block dispatcher
   が mid-paragraph トークンに到達しない)。Wikidot 互換の非貪欲 regex で
   parse 開始時に text-level で strip し、両ポジションで漏れない設計に変更。
   parse() に `stripDisabledHtmlBlocks` を追加。

3. (Low) disabled 時の contents 累積をスキップ
   disabled では body を破棄するため、token.value を contents に積む処理
   をガード。大きな入力でのメモリ/CPU を節約。

test: 段落内 inline 漏れ防止 + malformed close 漏れ防止 のケースを追加。
…pre-pass 廃止)

text-level pre-pass の問題点を洗い直して再設計:

1. (High) Parser class 直接使用で pre-pass がバイパスされる
2. (High) closed のみ strip、mid-paragraph unclosed が漏れる
3. (Medium) text-level pre-pass が [[code]]/[[html]] 等 raw 内も削除
4. (Medium) strip 後の lex で diagnostic 位置がずれる
5. (Medium) pre-pass の gate が truthiness、rule/render は === false

→ text-level pre-pass を廃止し、inline html rule 追加で再設計:
- packages/parser/src/parser/preprocess/strip-disabled-html.ts 削除
- parse() を元に戻す
- packages/parser/src/parser/rules/inline/html.ts (新規 htmlInlineRule):
  - allowHtmlBlocks !== false: success:false (従来挙動維持)
  - === false: [[html]]...[[/html]] を inline で消費、空要素返す
  - close-detection は BLOCK_CLOSE まで verify (whitespace 許容)
  - unclosed: NEWLINE+NEWLINE (blank line) で停止 (後続 paragraph 保護)
- block rule にも同じ blank-line 停止を追加
- inline rules 配列に登録 (commentRule の直後)

これで5件すべて解消:
1. rule 自体が parse 時に走るので Parser 直接使用でも gate される
2. inline rule が mid-paragraph closed/unclosed 両方扱う
3. [[code]]/[[html]] 内は block rule で raw 扱いされ inline rule は到達しない
4. source mutation なしで位置情報そのまま
5. すべて === false に統一

test 追加: Parser 直接使用 / blank-line で stop / code 内 [[html]] 保護
2 件のバグを修正:

1. (High) blank-line stop が enabled でも効き、空行を含む valid [[html]]
   block が page mode で壊れる。例: [[html]]\n<p>one</p>\n\n<p>two</p>\n[[/html]]
   → blank-line stop を disabled 時のみに限定。

2. (Medium) close detection は [[/html ]] (whitespace before ]]) を
   許容するが、close 消費側が whitespace を skip しておらず ]] が paragraph
   text に漏れていた。close 消費側にも whitespace skip を追加。

test: 空行入り valid html block / whitespace 付き close tag のケース追加。
disabled mode で blank-line stop が close より先に発火し、空行を含む
closed [[html]] (例: [[html]]\n<p>one</p>\n\n<p>two</p>\n[[/html]]) の
後半 <p>two</p> と [[/html]] が paragraph text に漏れていた。

修正: 事前の forward lookahead で real close 存在を確認する
lookaheadHasHtmlClose ヘルパを追加し、close が ahead に存在する場合は
blank-line stop を抑止。close 不在 (truly unclosed) の時のみ blank-line
stop が効く。block rule / inline rule 両方に適用。

regression test: disabled mode で空行入り closed block が完全に消費される
ことを確認。
@cloudflare-workers-and-pages

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 6fb375d Commit Preview URL

Branch Preview URL
Jun 03 2026, 03:51 AM

@cloudflare-workers-and-pages

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 6fb375d Commit Preview URL

Branch Preview URL
Jun 03 2026, 03:50 AM

@cloudflare-workers-and-pages

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 6fb375d Commit Preview URL

Branch Preview URL
Jun 03 2026, 03:50 AM

@r74tech
r74tech merged commit cba2d59 into develop Jun 4, 2026
9 checks passed
@r74tech
r74tech deleted the feat/settings-allow-html-blocks branch June 4, 2026 11:23
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