feat(parser): opener-embedded [[iftags]]をparse()で解決#47
Merged
Conversation
… を解決
[[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 を伝搬。
Deploying with
|
| 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 |
Deploying with
|
| 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 |
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.
背景
[[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)nullresolveModulesのgetPageTagsで評価される。[][[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挙動に合わせて以下を考慮:= "...") は同一行内のみ skip(packages/parser/src/lexer/lexer.tsの QUOTED_STRING 認識と同条件)[[[ ... ]]]triple-bracket link は LINK_OPEN/LINK_CLOSE として depth に算入しない[[xxxが後続の block-level[[iftags]]を巻き込まないようにする)wdmock-cf pipeline の整理
明示的
preprocessIftags呼び出しを削除し、parse(.., { pageTags })とresolveModulesのparsecallback に集約。挙動は不変。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 formatbun run typecheckbun 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 も全通過(回帰なし)parse(src, { pageTags })に差し替えて opener-embedded iftags が<div>として正しくレンダリングされることを実機確認