Skip to content

feat: component attribute handling and handler state scoping#49

Merged
mohamedmansour merged 7 commits into
mainfrom
mmansour/parser-phase2-components
Feb 24, 2026
Merged

feat: component attribute handling and handler state scoping#49
mohamedmansour merged 7 commits into
mainfrom
mmansour/parser-phase2-components

Conversation

@mohamedmansour

@mohamedmansour mohamedmansour commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

Parser

  • Rewrite component directive to process attributes individually with attrStart, attrSkip, and rawValue semantics
  • Unify process_tag_attributes for regular and component elements
  • Skip double-wrapping when component template already has <template>
  • Inject stylesheet <link> into component templates
  • Strip @/:/?-prefixed runtime attributes from template tags using tree-sitter
  • Support self-closing custom elements

Handler

  • Add resolve_value helper: checks local_vars first, then global state
  • Add evaluate_condition helper: merges local vars for expression evaluation
  • Nested for loop collection resolution (outerItem.innerItems)
  • Boolean attribute rendering (true/false/missing/multiple)
  • Simple/template attribute rendering with numeric/bool type support
  • Raw signal rendering (escaped vs unescaped)
  • For+if state scoping (local overrides global)
  • Component attribute state: accumulates attrs between attrStart and component fragment, makes them available as local vars inside the component template
  • camelCase conversion for hyphenated attribute names (data-titledataTitle)
  • Complex attribute (: prefix) state resolution — passes objects to component scope
  • Boolean attribute state forwarding to component templates
  • Scope isolation: component attrs don't pollute parent scope
  • Skipped attributes (attrSkip) excluded from component state

Tests

  • Parser: 81 tests
  • Handler: 26 tests (component attr state, boolean attrs, simple attrs, template attrs, raw signals, nested for, state scoping)

…ndling

Rewrite the component directive processing to match the Node.js reference
implementation for entry stream and component template stream handling.

Entry stream changes:
- Emit opening tag, attributes, closing bracket as separate fragments
- Add process_component_tag_attributes for component-specific attribute logic
- Static attrs on components emit as attribute fragments with rawValue
- First dynamic/static attribute gets attrStart marker
- Skipped attrs (class, style, role, data-*, aria-*) get attrSkip
- Process slot children with full directive support

Component template stream changes:
- Add process_component_template for shadow DOM wrapping
- Skip double-wrapping when template already has <template> tag
- Inject stylesheet link after opening template tag
- Strip @/:/?-prefixed runtime attributes from template tags
- Derive CSS path as {tag_name}.css when component has CSS content

Update existing tests and add new tests for:
- No double-wrap of existing template elements
- Styled component template with existing template tag
- Runtime attribute stripping from template tags
- Component with static attributes and slots
- Legacy components without styles
- Self-closing components with dynamic attributes
- Nested self-closing tags in component slots
- Boolean attribute with attrStart marking
- Meta/link tags with dynamic attributes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohamedmansour mohamedmansour force-pushed the mmansour/parser-phase2-components branch from d609cf2 to 7e3a3b4 Compare February 24, 2026 03:09
Merge the duplicated attribute-processing methods into a single
process_tag_attributes that accepts an is_component flag. Extract
maybe_mark_attr_start helper to set attr_start on the first
non-skipped component attribute.

Add test_component_attr_skip and test_component_multiple_nested
tests ported from the Node prototype.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohamedmansour mohamedmansour force-pushed the mmansour/parser-phase2-components branch from 7e3a3b4 to e8209ad Compare February 24, 2026 03:13
- Add resolve_value helper: checks local_vars first, then global state
- Add evaluate_condition helper: merges local vars into state for
  condition evaluation
- Fix process_for_loop to resolve collections from local vars
  (enables nested for loops like outerItem.innerItems)
- Fix process_attribute to resolve values from local vars and handle
  numeric/bool types properly
- Fix process_if to evaluate conditions against merged local+global state
- Simplify process_signal to delegate to resolve_value

13 new tests: boolean attrs (true/false/missing/multiple), simple attrs
(string/numeric), template attrs, raw signals, nested for loops,
for+if state scoping. 20 total handler tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohamedmansour mohamedmansour changed the title feat: rewrite process_component_directive with component-aware attribute feat: parser phase 2 + handler attribute/state improvements Feb 24, 2026
@mohamedmansour mohamedmansour changed the title feat: parser phase 2 + handler attribute/state improvements feat: component attribute handling and handler state scoping Feb 24, 2026
mohamedmansour and others added 4 commits February 23, 2026 20:22
- Add component_attrs field to WebUIProcessContext for accumulating
  attribute values between attrStart and the component fragment
- Add convert_hyphen_to_camel_case helper for data-* attribute names
- Update process_attribute to accumulate attrs: boolean, raw_value,
  dynamic, complex, and template attribute types
- Add render_template_attr_value for raw (unescaped) template rendering
- Update process_component to scope local_vars from accumulated attrs
  with proper save/restore for parent scope isolation
- Add component_attr_name helper for stripping : prefix and camelCase
- Add 6 tests covering simple, template, camelCase, complex, boolean,
  and parent scope isolation scenarios

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add fragment matcher DSL to webui-test-utils with constructors:
raw(), signal(), attr(), bool_attr(), attr_raw(), attr_skip(),
attr_complex(), component(), for_loop(), if_cond() and variants
with attr_start.

Macros assert_fragments! and assert_stream! provide:
- Count verification with descriptive fragment dump on mismatch
- Per-index field-level assertions with clear error messages
- Compact syntax mirroring the Node prototype test style

Migrate 3 parser tests as demonstration (15 lines → 5 lines each).
Remaining tests can be migrated incrementally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Convert 44 parser tests from verbose matches! patterns to the
(use .contains() patterns not supported by the macro).

matches! calls: 195 → 17
File size: 2601 → 2260 lines (-341)
All 81 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@mohamedmansour mohamedmansour requested a review from a team February 24, 2026 05:24
@mohamedmansour mohamedmansour merged commit 62c6ee9 into main Feb 24, 2026
5 checks passed
@mohamedmansour mohamedmansour deleted the mmansour/parser-phase2-components branch February 24, 2026 05:48
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.

2 participants