Skip to content

feat(parser): opener-embedded [[iftags]]をparse()で解決#47

Merged
r74tech merged 3 commits into
developfrom
feat/parse-page-tags-option
Jun 4, 2026
Merged

feat(parser): opener-embedded [[iftags]]をparse()で解決#47
r74tech merged 3 commits into
developfrom
feat/parse-page-tags-option

Conversation

@r74tech

@r74tech r74tech commented Jun 4, 2026

Copy link
Copy Markdown
Owner

背景

[[div_ class="x" [[iftags +foo]]style="display:none;"[[/iftags]]]] のようにブロックオープナーの属性リスト内に [[iftags ...]]X[[/iftags]] を埋め込んだ書式は、Wikidot の credit module 等で広く使われている。しかし現状の parse() 単独呼び出しでは、トークナイザが well-formed なオープナーを復元できず、[[div_]] 行全体が生テキストとして出力されていた。

text-level の preprocessIftags(source, pageTags) は元々用意されていたが、parse() から呼ばれない上に pageTags === null で完全 no-op だったため、タグが未取得の経路では opener-embedded iftags が必ず破綻していた。

変更点

ParserOptions.pageTags?: string[] | null を新規追加

parse(source, options) のオプションで text-level preprocess を起動する:

挙動
未指定 (undefined) preprocess を呼ばない。既存挙動と完全に同じ。
null opener-embedded iftags のみ空タグ仮定で text-level に畳む lossy fallback。block-level iftags は AST に残りresolveModulesgetPageTags で評価される。
[] 既知の空タグとして全 [[iftags]] を eager に評価。
string[] 既知のタグ集合で全 [[iftags]] を eager に評価。

options?.pageTags !== undefined でキー存在を判定するため、undefined を明示的に渡しても従来挙動を維持する(dynamic options 構築への配慮)。

preprocessIftags(source, null) の挙動変更

これまで完全 no-op だったのを「opener-embedded iftags のみ畳む」に変更。block-level iftags はそのまま残るため AST resolver の責務は不変。

depth 判定のオープナーコンテキスト識別

lexer の blockOpenerDepth 挙動に合わせて以下を考慮:

  • attribute quote (= "...") は同一行内のみ skip(packages/parser/src/lexer/lexer.ts の QUOTED_STRING 認識と同条件)
  • [[[ ... ]]] triple-bracket link は LINK_OPEN/LINK_CLOSE として depth に算入しない
  • 改行で depth をリセット(block opener は単一行構造のため、未閉 [[xxx が後続の block-level [[iftags]] を巻き込まないようにする)

wdmock-cf pipeline の整理

明示的 preprocessIftags 呼び出しを削除し、parse(.., { pageTags })resolveModulesparse callback に集約。挙動は不変。ListPages テンプレートの再 parse でも opener-embedded iftags を扱うため、callback 側にも pageTagsを伝搬している。

後方互換性

  • parse(src) / parse(src, {}) / parse(src, { settings, trackPositions })は挙動が一切変わらない。
  • preprocessIftags(source, []) / preprocessIftags(source, ["..."]) も従来通り。
  • preprocessIftags(source, null)意図的に挙動変更。これまでテストでno-op を期待していた箇所は description のみ更新し、入力が block-level のみであれば期待値はそのまま通る。

Test plan

  • bun run lint (10 warnings, 0 errors — 既存箇所のみ)
  • bun run format
  • bun run typecheck
  • bun test (1292 pass / 0 fail)
  • 新規テスト:
    • tests/unit/module/iftags/preprocess.test.ts: null モードでのopener-embedded 畳み込み、quoted attribute 内 ]] 保護、[[[ link 非干渉、ネスト iftags、改行 depth リセットの 7 ケース
    • tests/unit/parser/parser.test.ts: parse(src, { pageTags: ... })の挙動を pageTags の 4 値で検証する 4 ケース
  • tests/integration/fixture-*.test.ts の既存 fixture も全通過(回帰なし)
  • release 後、wpv4 / wdmock-cf 等の利用側で parse(src, { pageTags }) に差し替えて opener-embedded iftags が<div> として正しくレンダリングされることを実機確認

r74tech added 3 commits June 4, 2026 22:21
… を解決

[[div_ class="x" [[iftags +foo]]style="..."[[/iftags]]]] のように block opener
属性内に [[iftags]] を埋め込んだ書式は、トークナイザが well-formed な opener を
復元できず、これまで [[div_]] 行全体が生テキストとして出力されていた。

ParserOptions.pageTags を新規追加し、明示的に値が渡された時のみ
preprocessIftags を起動するよう parse() に内蔵する。

セマンティクス:
- 未指定 (undefined): 完全 skip。既存呼び出しの後方互換。
- null: opener-embedded [[iftags]] のみ空タグ仮定で text-level に畳む
  (lossy fallback)。block-level は AST に残し resolveIfTags に委譲。
- string[]: 全 [[iftags]] を当該タグで eager に評価する。

preprocessIftags(source, null) はこれまで完全 no-op だったが、上記方針に
合わせて opener-embedded のみ畳む挙動へ変更。

depth 判定は lexer の blockOpenerDepth に近づけるため、以下を考慮:
- attribute quote ("...") は = の直後でのみ skip (lexer.ts:579-591 相当)
- [[[ ... ]]] triple-bracket link は LINK_OPEN として depth に含めない
- 改行で depth をリセット (block opener は単一行構造のため、未閉 [[xxx が
  後続 block-level [[iftags]] を巻き込まないようにする)
- preprocess.test.ts: null モードでの opener-embedded 畳み込み、
  -tag / +tag 評価、quoted attribute 内の ]] 保護、[[[ link 非干渉、
  ネスト iftags、改行 depth リセットの 7 ケースを追加。
- parser.test.ts: parse(src, { pageTags: ... }) 経由で
  [[div_ class="x" [[iftags +highlight]]style="..."[[/iftags]]]]
  が container/div として正しくパースされることを 4 ケースで検証。
- preprocess-iftags-integration.test.ts: null pageTags 時の
  block-level [[iftags]] 保持を説明するコメントに更新。
明示的 preprocessIftags 呼び出しを削除し、parse(expanded, { pageTags }) と
resolveModules の parse callback に集約。挙動は不変。

ListPages テンプレート展開後の再 parse でも opener-embedded [[iftags]] が
出現する可能性があるため、callback 側 (parse: (input) => parse(input, { pageTags }).ast)
にも同じ pageTags を伝搬。
@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 f091e44 Commit Preview URL

Branch Preview URL
Jun 04 2026, 03:09 PM

@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 f091e44 Commit Preview URL

Branch Preview URL
Jun 04 2026, 03:09 PM

@r74tech
r74tech merged commit ee7dc49 into develop Jun 4, 2026
7 of 8 checks passed
@r74tech
r74tech deleted the feat/parse-page-tags-option branch June 4, 2026 15:11
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