@microsoft/fast-element 3.0.0-rc.1 → 3.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/README.md +50 -14
- package/dist/context/context.api.json +13 -13
- package/dist/declarative/declarative.api.json +654 -15
- package/dist/di/di.api.json +15 -15
- package/dist/dts/__test__/helpers.d.ts +6 -0
- package/dist/dts/__test__/setup-node.d.ts +0 -0
- package/dist/dts/binding/binding.d.ts +15 -5
- package/dist/dts/binding/one-time.d.ts +1 -1
- package/dist/dts/binding/one-way.d.ts +1 -1
- package/dist/dts/binding/signal.d.ts +1 -1
- package/dist/dts/binding/two-way.d.ts +1 -1
- package/dist/dts/components/attributes.d.ts +1 -1
- package/dist/dts/components/enable-hydration.d.ts +22 -2
- package/dist/dts/components/fast-definitions.d.ts +7 -4
- package/dist/dts/components/fast-element.d.ts +42 -12
- package/dist/dts/components/hydration-tracker.d.ts +47 -4
- package/dist/dts/components/hydration.d.ts +5 -0
- package/dist/dts/context.d.ts +7 -7
- package/dist/dts/declarative/debug.d.ts +2 -3
- package/dist/dts/declarative/index.d.ts +3 -2
- package/dist/dts/declarative/interfaces.d.ts +1 -2
- package/dist/dts/declarative/template.d.ts +2 -1
- package/dist/dts/declarative/utilities.d.ts +50 -4
- package/dist/dts/di/di.d.ts +6 -6
- package/dist/dts/dom-policy.d.ts +22 -4
- package/dist/dts/dom.d.ts +4 -16
- package/dist/dts/hydration/diagnostics.d.ts +93 -0
- package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
- package/dist/dts/hydration/messages.d.ts +62 -0
- package/dist/dts/hydration/target-builder.d.ts +26 -1
- package/dist/dts/hydration.d.ts +7 -3
- package/dist/dts/index.d.ts +7 -3
- package/dist/dts/interfaces.d.ts +1 -0
- package/dist/dts/observation/observable.d.ts +3 -3
- package/dist/dts/platform.d.ts +20 -4
- package/dist/dts/registry.d.ts +1 -0
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +1 -1
- package/dist/dts/templating/html-binding-directive.d.ts +6 -2
- package/dist/dts/templating/html-directive.d.ts +2 -1
- package/dist/dts/templating/hydration-view.d.ts +24 -3
- package/dist/dts/templating/ref.d.ts +1 -1
- package/dist/dts/templating/render.d.ts +2 -2
- package/dist/dts/templating/repeat.d.ts +1 -1
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +5 -5
- package/dist/dts/templating/when.d.ts +1 -1
- package/dist/dts/testing/fakes.d.ts +4 -4
- package/dist/esm/__test__/helpers.js +22 -0
- package/dist/esm/__test__/setup-node.js +18 -0
- package/dist/esm/binding/two-way.js +1 -2
- package/dist/esm/components/attributes.js +12 -8
- package/dist/esm/components/element-controller.js +11 -6
- package/dist/esm/components/enable-hydration.js +27 -3
- package/dist/esm/components/fast-definitions.js +19 -18
- package/dist/esm/components/hydration-tracker.js +34 -5
- package/dist/esm/components/hydration.js +85 -6
- package/dist/esm/debug.js +1 -0
- package/dist/esm/declarative/attribute-map.js +2 -1
- package/dist/esm/declarative/debug.js +0 -1
- package/dist/esm/declarative/index.js +1 -0
- package/dist/esm/declarative/interfaces.js +0 -1
- package/dist/esm/declarative/observer-map-utilities.js +58 -55
- package/dist/esm/declarative/template-bridge.js +4 -14
- package/dist/esm/declarative/template.js +4 -3
- package/dist/esm/declarative/utilities.js +236 -1
- package/dist/esm/di/di.js +2 -1
- package/dist/esm/dom-policy.js +33 -4
- package/dist/esm/hydration/diagnostics.js +50 -0
- package/dist/esm/hydration/hydration-debugger.js +112 -0
- package/dist/esm/hydration/messages.js +84 -0
- package/dist/esm/hydration/target-builder.js +65 -19
- package/dist/esm/hydration.js +3 -1
- package/dist/esm/index.js +6 -2
- package/dist/esm/interfaces.js +1 -0
- package/dist/esm/metadata.js +2 -8
- package/dist/esm/observation/notifier.js +2 -4
- package/dist/esm/registry.js +1 -0
- package/dist/esm/templating/html-binding-directive.js +1 -1
- package/dist/esm/templating/hydration-view.js +20 -27
- package/dist/esm/templating/render.js +39 -18
- package/dist/esm/templating/repeat.js +51 -17
- package/dist/esm/templating/view.js +1 -1
- package/dist/esm/testing/fixture.js +2 -2
- package/dist/esm/testing/timeout.js +2 -2
- package/dist/fast-element.api.json +1329 -99
- package/dist/fast-element.d.ts +147 -66
- package/dist/fast-element.debug.js +392 -99
- package/dist/fast-element.debug.min.js +2 -2
- package/dist/fast-element.js +392 -99
- package/dist/fast-element.min.js +2 -2
- package/dist/fast-element.untrimmed.d.ts +133 -70
- package/dist/hydration/hydration.api.json +1280 -57
- package/dist/styles/styles.api.json +1 -1
- package/package.json +21 -9
- package/ARCHITECTURE_FASTELEMENT.md +0 -63
- package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
- package/ARCHITECTURE_INTRO.md +0 -10
- package/ARCHITECTURE_OVERVIEW.md +0 -52
- package/ARCHITECTURE_UPDATES.md +0 -11
- package/CHANGELOG.json +0 -2275
- package/DECLARATIVE_DESIGN.md +0 -806
- package/DECLARATIVE_HTML.md +0 -470
- package/DECLARATIVE_MIGRATION.md +0 -215
- package/DECLARATIVE_RENDERING.md +0 -530
- package/DECLARATIVE_RENDERING_LIFECYCLE.md +0 -288
- package/DECLARATIVE_SCHEMA_OBSERVER_MAP.md +0 -489
- package/DESIGN.md +0 -615
- package/MIGRATION.md +0 -387
- package/SIZES.md +0 -25
- package/api-extractor.arrays.json +0 -15
- package/api-extractor.context.json +0 -15
- package/api-extractor.declarative.json +0 -15
- package/api-extractor.di.json +0 -15
- package/api-extractor.hydration.json +0 -15
- package/api-extractor.styles.json +0 -15
- package/biome.json +0 -4
- package/docs/ACKNOWLEDGEMENTS.md +0 -12
- package/docs/api-report.api.md +0 -1299
- package/docs/arrays/api-report.api.md +0 -114
- package/docs/context/api-report.api.md +0 -69
- package/docs/declarative/api-report.api.md +0 -397
- package/docs/di/api-report.api.md +0 -315
- package/docs/fast-element-2-changes.md +0 -15
- package/docs/hydration/api-report.api.md +0 -285
- package/docs/styles/api-report.api.md +0 -135
- package/playwright.config.ts +0 -26
- package/playwright.declarative.config.ts +0 -26
- package/playwright.declarative.webui.config.ts +0 -20
- package/scripts/declarative/build-fixtures-with-webui.js +0 -135
- package/scripts/declarative/build-fixtures.js +0 -49
- package/scripts/declarative/build-fixtures.utilities.js +0 -101
- package/scripts/measure-sizes.js +0 -219
- package/scripts/run-api-extractor.js +0 -70
- package/test/declarative/fixtures/README.md +0 -72
- package/test/declarative/fixtures/WRITING_FIXTURES.md +0 -330
- package/test/declarative/fixtures/bindings/README.md +0 -12
- package/test/declarative/fixtures/bindings/attribute/entry.html +0 -13
- package/test/declarative/fixtures/bindings/attribute/fast-build.config.json +0 -6
- package/test/declarative/fixtures/bindings/attribute/index.html +0 -25
- package/test/declarative/fixtures/bindings/attribute/main.ts +0 -41
- package/test/declarative/fixtures/bindings/attribute/state.json +0 -8
- package/test/declarative/fixtures/bindings/attribute/templates.html +0 -11
- package/test/declarative/fixtures/bindings/content/entry.html +0 -12
- package/test/declarative/fixtures/bindings/content/fast-build.config.json +0 -6
- package/test/declarative/fixtures/bindings/content/index.html +0 -19
- package/test/declarative/fixtures/bindings/content/main.ts +0 -27
- package/test/declarative/fixtures/bindings/content/state.json +0 -4
- package/test/declarative/fixtures/bindings/content/templates.html +0 -6
- package/test/declarative/fixtures/bindings/dot-syntax/entry.html +0 -11
- package/test/declarative/fixtures/bindings/dot-syntax/fast-build.config.json +0 -6
- package/test/declarative/fixtures/bindings/dot-syntax/index.html +0 -47
- package/test/declarative/fixtures/bindings/dot-syntax/main.ts +0 -59
- package/test/declarative/fixtures/bindings/dot-syntax/state.json +0 -16
- package/test/declarative/fixtures/bindings/dot-syntax/templates.html +0 -17
- package/test/declarative/fixtures/bindings/event/entry.html +0 -11
- package/test/declarative/fixtures/bindings/event/fast-build.config.json +0 -6
- package/test/declarative/fixtures/bindings/event/index.html +0 -43
- package/test/declarative/fixtures/bindings/event/main.ts +0 -43
- package/test/declarative/fixtures/bindings/event/state.json +0 -3
- package/test/declarative/fixtures/bindings/event/templates.html +0 -18
- package/test/declarative/fixtures/bindings/host/entry.html +0 -40
- package/test/declarative/fixtures/bindings/host/fast-build.config.json +0 -6
- package/test/declarative/fixtures/bindings/host/index.html +0 -96
- package/test/declarative/fixtures/bindings/host/main.ts +0 -222
- package/test/declarative/fixtures/bindings/host/state.json +0 -9
- package/test/declarative/fixtures/bindings/host/templates.html +0 -55
- package/test/declarative/fixtures/directives/README.md +0 -12
- package/test/declarative/fixtures/directives/children/entry.html +0 -11
- package/test/declarative/fixtures/directives/children/fast-build.config.json +0 -6
- package/test/declarative/fixtures/directives/children/index.html +0 -15
- package/test/declarative/fixtures/directives/children/main.ts +0 -22
- package/test/declarative/fixtures/directives/children/state.json +0 -3
- package/test/declarative/fixtures/directives/children/templates.html +0 -3
- package/test/declarative/fixtures/directives/ref/entry.html +0 -11
- package/test/declarative/fixtures/directives/ref/fast-build.config.json +0 -6
- package/test/declarative/fixtures/directives/ref/index.html +0 -15
- package/test/declarative/fixtures/directives/ref/main.ts +0 -17
- package/test/declarative/fixtures/directives/ref/state.json +0 -1
- package/test/declarative/fixtures/directives/ref/templates.html +0 -3
- package/test/declarative/fixtures/directives/repeat/entry.html +0 -21
- package/test/declarative/fixtures/directives/repeat/fast-build.config.json +0 -6
- package/test/declarative/fixtures/directives/repeat/index.html +0 -133
- package/test/declarative/fixtures/directives/repeat/main.ts +0 -110
- package/test/declarative/fixtures/directives/repeat/sprites.svg +0 -8
- package/test/declarative/fixtures/directives/repeat/state.json +0 -10
- package/test/declarative/fixtures/directives/repeat/templates.html +0 -75
- package/test/declarative/fixtures/directives/slotted/entry.html +0 -17
- package/test/declarative/fixtures/directives/slotted/fast-build.config.json +0 -6
- package/test/declarative/fixtures/directives/slotted/index.html +0 -27
- package/test/declarative/fixtures/directives/slotted/main.ts +0 -29
- package/test/declarative/fixtures/directives/slotted/state.json +0 -1
- package/test/declarative/fixtures/directives/slotted/templates.html +0 -7
- package/test/declarative/fixtures/directives/when/entry.html +0 -51
- package/test/declarative/fixtures/directives/when/fast-build.config.json +0 -6
- package/test/declarative/fixtures/directives/when/index.html +0 -136
- package/test/declarative/fixtures/directives/when/main.ts +0 -172
- package/test/declarative/fixtures/directives/when/state.json +0 -12
- package/test/declarative/fixtures/directives/when/templates.html +0 -75
- package/test/declarative/fixtures/ecosystem/README.md +0 -11
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/entry.html +0 -12
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/fast-build.config.json +0 -6
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/index.html +0 -20
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/main.ts +0 -68
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/state.json +0 -4
- package/test/declarative/fixtures/ecosystem/declarative-no-hydration/templates.html +0 -7
- package/test/declarative/fixtures/ecosystem/errors/entry.html +0 -12
- package/test/declarative/fixtures/ecosystem/errors/fast-build.config.json +0 -6
- package/test/declarative/fixtures/ecosystem/errors/index.html +0 -20
- package/test/declarative/fixtures/ecosystem/errors/main.ts +0 -17
- package/test/declarative/fixtures/ecosystem/errors/state.json +0 -1
- package/test/declarative/fixtures/ecosystem/errors/templates.html +0 -7
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/entry.html +0 -17
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/fast-build.config.json +0 -6
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/index.html +0 -56
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/main.ts +0 -134
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/state.json +0 -12
- package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/templates.html +0 -34
- package/test/declarative/fixtures/ecosystem/performance-metrics/entry.html +0 -25
- package/test/declarative/fixtures/ecosystem/performance-metrics/fast-build.config.json +0 -6
- package/test/declarative/fixtures/ecosystem/performance-metrics/fast-card.css +0 -10
- package/test/declarative/fixtures/ecosystem/performance-metrics/index.html +0 -181
- package/test/declarative/fixtures/ecosystem/performance-metrics/main.ts +0 -58
- package/test/declarative/fixtures/ecosystem/performance-metrics/state.json +0 -6
- package/test/declarative/fixtures/ecosystem/performance-metrics/templates.html +0 -15
- package/test/declarative/fixtures/extensions/README.md +0 -15
- package/test/declarative/fixtures/extensions/attribute-map/entry.html +0 -14
- package/test/declarative/fixtures/extensions/attribute-map/fast-build.config.json +0 -6
- package/test/declarative/fixtures/extensions/attribute-map/index.html +0 -31
- package/test/declarative/fixtures/extensions/attribute-map/main.ts +0 -40
- package/test/declarative/fixtures/extensions/attribute-map/state.json +0 -4
- package/test/declarative/fixtures/extensions/attribute-map/templates.html +0 -14
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/entry.html +0 -12
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/fast-build.config.json +0 -7
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/index.html +0 -25
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/main.ts +0 -31
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/state.json +0 -5
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/templates.html +0 -11
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/entry.html +0 -13
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/fast-build.config.json +0 -7
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/index.html +0 -23
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/main.ts +0 -37
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/state.json +0 -1
- package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/templates.html +0 -9
- package/test/declarative/fixtures/extensions/observer-map/entry.html +0 -15
- package/test/declarative/fixtures/extensions/observer-map/fast-build.config.json +0 -6
- package/test/declarative/fixtures/extensions/observer-map/index.html +0 -442
- package/test/declarative/fixtures/extensions/observer-map/main.ts +0 -482
- package/test/declarative/fixtures/extensions/observer-map/state.json +0 -158
- package/test/declarative/fixtures/extensions/observer-map/templates.html +0 -172
- package/test/declarative/fixtures/extensions/observer-map-config-object/entry.html +0 -16
- package/test/declarative/fixtures/extensions/observer-map-config-object/fast-build.config.json +0 -6
- package/test/declarative/fixtures/extensions/observer-map-config-object/index.html +0 -27
- package/test/declarative/fixtures/extensions/observer-map-config-object/main.ts +0 -53
- package/test/declarative/fixtures/extensions/observer-map-config-object/state.json +0 -9
- package/test/declarative/fixtures/extensions/observer-map-config-object/templates.html +0 -12
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/README.md +0 -98
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/entry.html +0 -156
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/fast-build.config.json +0 -6
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/index.html +0 -376
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/main.ts +0 -366
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/state.json +0 -69
- package/test/declarative/fixtures/extensions/observer-map-deep-merge/templates.html +0 -91
- package/test/declarative/fixtures/extensions/observer-map-properties/entry.html +0 -14
- package/test/declarative/fixtures/extensions/observer-map-properties/fast-build.config.json +0 -6
- package/test/declarative/fixtures/extensions/observer-map-properties/index.html +0 -110
- package/test/declarative/fixtures/extensions/observer-map-properties/main.ts +0 -175
- package/test/declarative/fixtures/extensions/observer-map-properties/state.json +0 -29
- package/test/declarative/fixtures/extensions/observer-map-properties/templates.html +0 -55
- package/test/declarative/fixtures/scenarios/README.md +0 -7
- package/test/declarative/fixtures/scenarios/nested-elements/entry.html +0 -16
- package/test/declarative/fixtures/scenarios/nested-elements/fast-build.config.json +0 -6
- package/test/declarative/fixtures/scenarios/nested-elements/index.html +0 -126
- package/test/declarative/fixtures/scenarios/nested-elements/main.ts +0 -214
- package/test/declarative/fixtures/scenarios/nested-elements/state.json +0 -10
- package/test/declarative/fixtures/scenarios/nested-elements/templates.html +0 -54
- package/test/declarative/index.html +0 -12
- package/test/declarative/vite.config.ts +0 -55
- package/test/declarative-main.ts +0 -6
- package/test/extension-subpaths-main.ts +0 -9
- package/test/index.html +0 -11
- package/test/main.ts +0 -109
- package/test/pure-declarative-main.ts +0 -1
- package/test/vite.config.ts +0 -19
- package/tsconfig.api-extractor.json +0 -6
package/DECLARATIVE_DESIGN.md
DELETED
|
@@ -1,806 +0,0 @@
|
|
|
1
|
-
# Declarative HTML Design
|
|
2
|
-
|
|
3
|
-
This document is intended for contributors who want to understand the internal
|
|
4
|
-
architecture of the declarative runtime and schema-driven map extensions in
|
|
5
|
-
`@microsoft/fast-element`. It covers the feature's purpose, core concepts, data
|
|
6
|
-
flow, and its integration with the rest of `@microsoft/fast-element`.
|
|
7
|
-
|
|
8
|
-
## Table of Contents
|
|
9
|
-
|
|
10
|
-
1. [Overview](#overview)
|
|
11
|
-
2. [Goals](#goals)
|
|
12
|
-
3. [Core Concepts](#core-concepts)
|
|
13
|
-
4. [Package Structure](#package-structure)
|
|
14
|
-
5. [Exports and Public API](#exports-and-public-api)
|
|
15
|
-
6. [Template Syntax](#template-syntax)
|
|
16
|
-
7. [Data Flow](#data-flow)
|
|
17
|
-
8. [Template Parsing Pipeline](#template-parsing-pipeline)
|
|
18
|
-
9. [Schema and Observer Map](#schema-and-observer-map)
|
|
19
|
-
10. [Lifecycle](#lifecycle)
|
|
20
|
-
11. [Integration with fast-element](#integration-with-fast-element)
|
|
21
|
-
12. [Hydration Model](#hydration-model)
|
|
22
|
-
13. [Testing Architecture](#testing-architecture)
|
|
23
|
-
14. [Further Reading](#further-reading)
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## Overview
|
|
28
|
-
|
|
29
|
-
`@microsoft/fast-element` lets you write FAST Web Component
|
|
30
|
-
templates as plain HTML rather than JavaScript `html` tagged template literals.
|
|
31
|
-
The browser-side JS bundle defines FAST's internal native `<f-template>`
|
|
32
|
-
publisher on demand. It parses declarative template markup at runtime and
|
|
33
|
-
returns a `ViewTemplate` to the waiting FAST element definition through the
|
|
34
|
-
registry-aware declarative template bridge.
|
|
35
|
-
|
|
36
|
-
```html
|
|
37
|
-
<!-- Declarative template — stack-agnostic, no JS needed to render -->
|
|
38
|
-
<my-component greeting="Hello">
|
|
39
|
-
<template shadowrootmode="open">
|
|
40
|
-
<!--fe:b-->Hello<!--fe:/b-->
|
|
41
|
-
</template>
|
|
42
|
-
</my-component>
|
|
43
|
-
|
|
44
|
-
<!-- Template definition — parsed once by the browser bundle -->
|
|
45
|
-
<f-template name="my-component">
|
|
46
|
-
<template>{{greeting}}</template>
|
|
47
|
-
</f-template>
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Goals
|
|
53
|
-
|
|
54
|
-
| Goal | Description |
|
|
55
|
-
|---|---|
|
|
56
|
-
| **Server-agnostic rendering** | Templates are plain HTML strings with no dependency on Node.js or any specific SSR framework. |
|
|
57
|
-
| **Progressive enhancement** | Components can be server-rendered and then hydrated client-side without a full re-render. |
|
|
58
|
-
| **FAST parity** | The declarative syntax maps 1-to-1 to FAST Element directive helpers (`repeat`, `when`, `slotted`, `children`, `ref`) exported from `@microsoft/fast-element`. |
|
|
59
|
-
| **Minimal authoring overhead** | Component authors write HTML, not tagged template strings, while retaining full reactive capabilities. |
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Core Concepts
|
|
64
|
-
|
|
65
|
-
### `<f-template>` — the internal template publisher
|
|
66
|
-
|
|
67
|
-
`<f-template>` is an internal custom element implemented as a lightweight native
|
|
68
|
-
`HTMLElement`. It is defined automatically by `declarativeTemplate()` in the same
|
|
69
|
-
`CustomElementRegistry` as the FAST element definition. Consumers should not
|
|
70
|
-
import, subclass, or define the implementation directly.
|
|
71
|
-
|
|
72
|
-
When connected to the DOM it:
|
|
73
|
-
|
|
74
|
-
1. Registers itself with the declarative template bridge using its registry and
|
|
75
|
-
`name` attribute.
|
|
76
|
-
2. Publishes a template when a matching FAST element definition requests one.
|
|
77
|
-
3. Delegates parsing of the inner `<template>` tag to `TemplateParser`, which
|
|
78
|
-
converts declarative bindings into FAST `ViewTemplate` strings and values.
|
|
79
|
-
4. Runs definition-scoped schema transforms, such as `attributeMap()` and
|
|
80
|
-
`observerMap()`, before returning the concrete `ViewTemplate`.
|
|
81
|
-
|
|
82
|
-
### `TemplateParser` — declarative HTML parser
|
|
83
|
-
|
|
84
|
-
A standalone class that converts declarative HTML template markup into the
|
|
85
|
-
`strings` and `values` arrays that `ViewTemplate.create()` consumes. It is used
|
|
86
|
-
by the internal `<f-template>` publisher but can also be used independently for
|
|
87
|
-
programmatic template compilation. The parsing pipeline is fully synchronous —
|
|
88
|
-
no promises are allocated during template resolution. A `StringsAccumulator`
|
|
89
|
-
tracks the running concatenation of preceding HTML, eliminating repeated O(N)
|
|
90
|
-
`join("")` calls at each binding site. `createTemplate()` lazily installs the
|
|
91
|
-
declarative runtime's debug messages; hydration support is installed only by
|
|
92
|
-
`enableHydration()`.
|
|
93
|
-
|
|
94
|
-
### `Schema` — JSON schema builder
|
|
95
|
-
|
|
96
|
-
Built during declarative template parsing, one `Schema` instance per
|
|
97
|
-
`<f-template>`. Non-declarative callers can also create and attach a `Schema`
|
|
98
|
-
manually. It records every binding path discovered in the template or supplied
|
|
99
|
-
by code and constructs a JSON Schema-compatible data structure. This schema:
|
|
100
|
-
|
|
101
|
-
- Describes the shape of each root property referenced in the template.
|
|
102
|
-
- Tracks repeat context chains (parent/child array relationships).
|
|
103
|
-
- Uses an instance-level `schemaMap` for its own property schemas.
|
|
104
|
-
- Registers itself in the module-level `schemaRegistry` (keyed by custom element name) for cross-element `$ref` resolution.
|
|
105
|
-
|
|
106
|
-
`FASTElementDefinition.schema` is optional. `declarativeTemplate()` assigns it
|
|
107
|
-
automatically after parsing; manual schema users can pass `schema` in the
|
|
108
|
-
definition object.
|
|
109
|
-
|
|
110
|
-
### `ObserverMap` — automatic observable setup
|
|
111
|
-
|
|
112
|
-
An optional layer that uses the `Schema` to automatically:
|
|
113
|
-
|
|
114
|
-
- Call `Observable.defineProperty()` for every root property on the element prototype.
|
|
115
|
-
- Install property-change handlers that wrap newly assigned objects/arrays in `Proxy` instances.
|
|
116
|
-
- Propagate deep property mutations back through FAST's observable system so bindings re-render.
|
|
117
|
-
|
|
118
|
-
Enabled via the `observerMap()` definition extension exported from
|
|
119
|
-
`@microsoft/fast-element/observer-map.js`. Calling `observerMap()` without
|
|
120
|
-
arguments observes all root properties discovered in the template or supplied
|
|
121
|
-
schema.
|
|
122
|
-
|
|
123
|
-
#### Path-level observation control
|
|
124
|
-
|
|
125
|
-
The `ObserverMapConfig` interface accepts an optional `schema` for
|
|
126
|
-
non-declarative/manual schema use and an optional `properties` key that maps
|
|
127
|
-
root property names to a recursive path tree controlling observation
|
|
128
|
-
granularity:
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
MyElement.define(
|
|
132
|
-
{
|
|
133
|
-
name: "my-element",
|
|
134
|
-
template: declarativeTemplate(),
|
|
135
|
-
},
|
|
136
|
-
[
|
|
137
|
-
observerMap({
|
|
138
|
-
properties: {
|
|
139
|
-
user: {
|
|
140
|
-
name: true, // user.name — observed
|
|
141
|
-
details: {
|
|
142
|
-
age: true, // user.details.age — observed
|
|
143
|
-
history: false, // user.details.history — NOT observed
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
// root properties not listed here are skipped
|
|
147
|
-
},
|
|
148
|
-
}),
|
|
149
|
-
],
|
|
150
|
-
);
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
Each path entry can be:
|
|
154
|
-
- **`true`** — observe this path and all descendants (unless overridden deeper).
|
|
155
|
-
- **`false`** — skip this path and all descendants (unless overridden deeper).
|
|
156
|
-
- **`ObserverMapPathNode`** — an object with an optional `$observe` boolean and child property overrides, allowing alternating opt-in/opt-out to arbitrary depth.
|
|
157
|
-
|
|
158
|
-
When `properties` is omitted, all root properties are observed. When
|
|
159
|
-
`properties` is present but empty (`{ properties: {} }`), no root properties
|
|
160
|
-
are observed.
|
|
161
|
-
|
|
162
|
-
For non-declarative elements, pass a schema directly to `observerMap()`:
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
import { FASTElement, Schema } from "@microsoft/fast-element";
|
|
166
|
-
import { observerMap } from "@microsoft/fast-element/observer-map.js";
|
|
167
|
-
|
|
168
|
-
class MyElement extends FASTElement {}
|
|
169
|
-
|
|
170
|
-
const schema = new Schema("my-element");
|
|
171
|
-
schema.addPath({
|
|
172
|
-
rootPropertyName: "user",
|
|
173
|
-
pathConfig: {
|
|
174
|
-
type: "default",
|
|
175
|
-
parentContext: null,
|
|
176
|
-
currentContext: null,
|
|
177
|
-
path: "user.name",
|
|
178
|
-
},
|
|
179
|
-
childrenMap: null,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
MyElement.define({ name: "my-element" }, [observerMap({ schema })]);
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The resolution algorithm walks the schema and configuration tree in parallel:
|
|
186
|
-
1. If `properties` is present and a root property is not listed, it is skipped.
|
|
187
|
-
2. `true`/`false` booleans apply to the entire subtree.
|
|
188
|
-
3. `$observe` on a node object controls the current level; children inherit when unspecified.
|
|
189
|
-
4. Paths in the config but not in the schema are silently ignored (forward-compatible).
|
|
190
|
-
|
|
191
|
-
### `AttributeMap` — automatic `@attr` definitions
|
|
192
|
-
|
|
193
|
-
An optional layer that uses the `Schema` to automatically register `@attr`-style reactive properties for every **leaf binding** in the template — i.e. simple expressions like `{{foo}}` or `id="{{foo-bar}}"` that have no nested properties, no explicit type, and no child element references.
|
|
194
|
-
|
|
195
|
-
- By default (`attribute-name-strategy: "camelCase"`), the binding key is treated as a camelCase property name and the HTML attribute name is derived by converting it to kebab-case (e.g. `{{fooBar}}` → property `fooBar`, attribute `foo-bar`). This matches the build-time `--attribute-name-strategy` option in `@microsoft/fast-build`.
|
|
196
|
-
- When `attribute-name-strategy` is `"none"`, the **attribute name** and **property name** are both the binding key exactly as written in the template (e.g. `{{foo-bar}}` → attribute `foo-bar`, property `foo-bar`). No normalization is applied.
|
|
197
|
-
- Because HTML attributes are case-insensitive, binding keys should use lowercase names (optionally dash-separated) when using the `"none"` strategy.
|
|
198
|
-
- Properties already decorated with `@attr` or `@observable` are left untouched.
|
|
199
|
-
- `FASTElementDefinition.attributeLookup` is keyed by the HTML attribute name, and `propertyLookup` is keyed by the JS property name so `attributeChangedCallback` correctly delegates to the new `AttributeDefinition`.
|
|
200
|
-
|
|
201
|
-
Enabled via the `attributeMap()` definition extension exported from
|
|
202
|
-
`@microsoft/fast-element/attribute-map.js`. Calling `attributeMap()` without
|
|
203
|
-
arguments uses the default `"camelCase"` strategy. To preserve binding keys
|
|
204
|
-
exactly as written, pass `attributeMap({ "attribute-name-strategy": "none" })`.
|
|
205
|
-
Outside declarative templates, `attributeMap()` uses the optional `schema` on the
|
|
206
|
-
FAST element definition.
|
|
207
|
-
|
|
208
|
-
### Syntax constants (`syntax.ts`)
|
|
209
|
-
|
|
210
|
-
All delimiters used by the parser are defined in a single `Syntax` interface and exported as named constants from `syntax.ts`. This makes the syntax pluggable and easy to audit.
|
|
211
|
-
|
|
212
|
-
| Constant | Value | Use |
|
|
213
|
-
|---|---|---|
|
|
214
|
-
| `openExpression` / `closeExpression` | `{{` / `}}` | Default (SSR-compatible) binding |
|
|
215
|
-
| `unescapedOpenExpression` / `unescapedCloseExpression` | `{{{` / `}}}` | Raw HTML binding |
|
|
216
|
-
| `clientSideOpenExpression` / `clientSideCloseExpression` | `{` / `}` | Client-only (event / attribute directive) binding |
|
|
217
|
-
| `repeatDirectiveOpen` / `repeatDirectiveClose` | `<f-repeat` / `</f-repeat>` | Repeat directive |
|
|
218
|
-
| `whenDirectiveOpen` / `whenDirectiveClose` | `<f-when` / `</f-when>` | When directive |
|
|
219
|
-
| `attributeDirectivePrefix` | `f-` | Attribute directive prefix |
|
|
220
|
-
| `eventArgAccessor` | `$e` | DOM event argument |
|
|
221
|
-
| `executionContextAccessor` | `$c` | Execution context argument |
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## Package Structure
|
|
226
|
-
|
|
227
|
-
```
|
|
228
|
-
packages/fast-element/
|
|
229
|
-
├── src/
|
|
230
|
-
│ ├── components/
|
|
231
|
-
│ │ ├── schema.ts # Shared Schema class + schemaRegistry
|
|
232
|
-
│ │ └── definition-schema-transforms.ts # Definition-scoped schema transform storage
|
|
233
|
-
│ └── declarative/
|
|
234
|
-
│ ├── index.ts # Public declarative entrypoint implementation
|
|
235
|
-
│ ├── interfaces.ts # Message enum (error codes)
|
|
236
|
-
│ ├── debug.ts # Human-readable declarative debug messages
|
|
237
|
-
│ ├── template.ts # declarativeTemplate(), internal <f-template> publisher, lifecycle orchestration
|
|
238
|
-
│ ├── template-bridge.ts # Registry/name bridge between definitions and publishers
|
|
239
|
-
│ ├── template-parser.ts # TemplateParser — converts declarative HTML to ViewTemplate strings/values
|
|
240
|
-
│ ├── observer-map.ts # observerMap() implementation
|
|
241
|
-
│ ├── attribute-map.ts # attributeMap() implementation
|
|
242
|
-
│ ├── observer-map-utilities.ts # Shared observer-map helpers
|
|
243
|
-
│ ├── utilities.ts # Declarative parsing helpers
|
|
244
|
-
│ └── syntax.ts # Syntax delimiter constants
|
|
245
|
-
├── scripts/
|
|
246
|
-
│ └── declarative/ # Fixture build + webui integration scripts
|
|
247
|
-
└── test/
|
|
248
|
-
└── declarative/fixtures/ # One directory per feature, each with spec + index.html + main.ts
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Module dependency direction
|
|
252
|
-
|
|
253
|
-
The default `declarativeTemplate()` path avoids importing optional map
|
|
254
|
-
implementations. Map helpers attach schema transforms to the definition only
|
|
255
|
-
when the consumer passes them as define extensions.
|
|
256
|
-
|
|
257
|
-
```
|
|
258
|
-
declarative/template.ts ──imports──▶ components/definition-schema-transforms.ts (schema transform reads)
|
|
259
|
-
declarative/template.ts ──imports──▶ components/schema.ts (Schema)
|
|
260
|
-
declarative/attribute-map.ts ──imports──▶ components/definition-schema-transforms.ts (register attribute-map transform)
|
|
261
|
-
declarative/observer-map.ts ──imports──▶ components/definition-schema-transforms.ts (register observer-map transform)
|
|
262
|
-
declarative/observer-map.ts ──imports──▶ components/schema.ts (Schema types)
|
|
263
|
-
declarative/attribute-map.ts ──imports──▶ components/schema.ts (Schema types)
|
|
264
|
-
declarative/utilities.ts ──imports──▶ components/schema.ts (schemaRegistry for cross-element $ref resolution)
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
Schema transforms run in deterministic order. `attributeMap()` runs before
|
|
268
|
-
`observerMap()`, so generated attributes are available to observer mapping.
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Exports and Public API
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import {
|
|
276
|
-
declarativeTemplate,
|
|
277
|
-
TemplateParser,
|
|
278
|
-
type ResolvedStringsAndValues,
|
|
279
|
-
} from "@microsoft/fast-element/declarative.js";
|
|
280
|
-
import {
|
|
281
|
-
Schema,
|
|
282
|
-
schemaRegistry,
|
|
283
|
-
type CachedPathMap,
|
|
284
|
-
type JSONSchema,
|
|
285
|
-
} from "@microsoft/fast-element/schema.js";
|
|
286
|
-
import {
|
|
287
|
-
type FASTElementExtension,
|
|
288
|
-
type TemplateLifecycleCallbacks,
|
|
289
|
-
} from "@microsoft/fast-element";
|
|
290
|
-
import {
|
|
291
|
-
attributeMap,
|
|
292
|
-
type AttributeMapConfig,
|
|
293
|
-
} from "@microsoft/fast-element/attribute-map.js";
|
|
294
|
-
import {
|
|
295
|
-
observerMap,
|
|
296
|
-
type ObserverMapConfig,
|
|
297
|
-
type ObserverMapPathEntry,
|
|
298
|
-
type ObserverMapPathNode,
|
|
299
|
-
} from "@microsoft/fast-element/observer-map.js";
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Declarative template helpers, map helpers, and their configuration types are
|
|
303
|
-
available from focused path exports.
|
|
304
|
-
|
|
305
|
-
Primary declarative exports intended for application code:
|
|
306
|
-
|
|
307
|
-
| Export | Purpose |
|
|
308
|
-
|---|---|
|
|
309
|
-
| `declarativeTemplate()` | `@microsoft/fast-element/declarative.js` template resolver for `FASTElement.define()`; auto-defines the internal `<f-template>` publisher and waits for the matching template. |
|
|
310
|
-
| `TemplateParser` | `@microsoft/fast-element/declarative.js` standalone parser that converts declarative HTML into `ViewTemplate` strings/values. Can be used independently of `<f-template>` for programmatic template compilation. |
|
|
311
|
-
| `Schema` | `@microsoft/fast-element/schema.js` JSON schema builder that records binding paths discovered during template parsing. Each instance owns its own schema map and registers itself in the `schemaRegistry` for cross-element `$ref` resolution. |
|
|
312
|
-
| `schemaRegistry` | `@microsoft/fast-element/schema.js` module-level `Map<string, Map<string, JSONSchema>>` that indexes schemas by custom element name. Used for cross-element lookups (e.g. nested component `$ref` resolution). |
|
|
313
|
-
|
|
314
|
-
Primary map extension exports:
|
|
315
|
-
|
|
316
|
-
| Export | Import path | Purpose |
|
|
317
|
-
|---|---|---|
|
|
318
|
-
| `attributeMap()` | `@microsoft/fast-element/attribute-map.js` | Define extension that registers `@attr`-style properties for leaf bindings discovered during parsing or supplied by `definition.schema`. |
|
|
319
|
-
| `observerMap()` | `@microsoft/fast-element/observer-map.js` | Define extension that defines observable root properties and proxy-based deep change tracking from `config.schema`, `definition.schema`, or a declarative template schema. |
|
|
320
|
-
|
|
321
|
-
The implementation element class (`<f-template>`), `TemplateElement.config()`,
|
|
322
|
-
`TemplateElement.options()`, `ElementOptions*`, and
|
|
323
|
-
`HydrationLifecycleCallbacks` are not exported from the public declarative
|
|
324
|
-
entrypoint. Use `declarativeTemplate()`, `attributeMap()`, `observerMap()`, and
|
|
325
|
-
`enableHydration()` instead. The `AttributeMap` and `ObserverMap` implementation
|
|
326
|
-
classes are available for advanced scenarios, but application code should
|
|
327
|
-
normally use the extension factories.
|
|
328
|
-
|
|
329
|
-
Additionally, `@microsoft/fast-element` exports these types:
|
|
330
|
-
|
|
331
|
-
| Type | Source Module | Purpose |
|
|
332
|
-
|---|---|---|
|
|
333
|
-
| `FASTElementExtension` | `fast-definitions.ts` | Definition extension callback signature used by `attributeMap()` and `observerMap()`. |
|
|
334
|
-
| `TemplateLifecycleCallbacks` | `fast-definitions.ts` | Per-element lifecycle callbacks accepted by `declarativeTemplate()`. |
|
|
335
|
-
| `JSONSchema` | `components/schema.ts` | JSON Schema interface used by `Schema` for property structure. |
|
|
336
|
-
| `CachedPathMap` | `components/schema.ts` | `Map<string, Map<string, JSONSchema>>` — the shape of the schema registry. |
|
|
337
|
-
|
|
338
|
-
The map configuration types are exported from the same map-specific paths:
|
|
339
|
-
|
|
340
|
-
| Type | Import path | Purpose |
|
|
341
|
-
|---|---|---|
|
|
342
|
-
| `ObserverMapConfig` | `@microsoft/fast-element/observer-map.js` | Configuration object for `observerMap()`; accepts optional `schema` and `properties` keys. |
|
|
343
|
-
| `ObserverMapPathEntry` | `@microsoft/fast-element/observer-map.js` | `boolean \| ObserverMapPathNode` — a node in the observation path tree. |
|
|
344
|
-
| `ObserverMapPathNode` | `@microsoft/fast-element/observer-map.js` | Object node with optional `$observe` and child property overrides. |
|
|
345
|
-
| `AttributeMapConfig` | `@microsoft/fast-element/attribute-map.js` | Configuration object for `attributeMap()`; accepts `attribute-name-strategy`. |
|
|
346
|
-
|
|
347
|
-
---
|
|
348
|
-
|
|
349
|
-
## Template Syntax
|
|
350
|
-
|
|
351
|
-
The declarative syntax is a superset of HTML with three binding delimiters:
|
|
352
|
-
|
|
353
|
-
| Syntax | Example | Behaviour |
|
|
354
|
-
|---|---|---|
|
|
355
|
-
| `{{expr}}` | `{{greeting}}` | SSR-compatible content / attribute binding |
|
|
356
|
-
| `{{{expr}}}` | `{{{rawHtml}}}` | Unescaped HTML (wraps in `<div :innerHTML>`) |
|
|
357
|
-
| `{expr}` | `@click="{handleClick($e)}"` | Client-only binding (events, attribute directives) |
|
|
358
|
-
|
|
359
|
-
### Directives
|
|
360
|
-
|
|
361
|
-
| Directive | Example |
|
|
362
|
-
|---|---|
|
|
363
|
-
| `<f-when value="{{expr}}">` | Conditional rendering |
|
|
364
|
-
| `<f-repeat value="{{item in list}}">` | List rendering |
|
|
365
|
-
| `f-slotted="{prop}"` | Slotted nodes attribute directive |
|
|
366
|
-
| `f-children="{prop}"` | Children attribute directive |
|
|
367
|
-
| `f-ref="{prop}"` | Element ref attribute directive |
|
|
368
|
-
|
|
369
|
-
For full syntax reference see [README.md](./README.md).
|
|
370
|
-
|
|
371
|
-
---
|
|
372
|
-
|
|
373
|
-
## Data Flow
|
|
374
|
-
|
|
375
|
-
The high-level data flow from authoring to interactive component:
|
|
376
|
-
|
|
377
|
-
```mermaid
|
|
378
|
-
flowchart TD
|
|
379
|
-
A["Author writes declarative HTML
|
|
380
|
-
using f-template with binding expressions"] --> B["Server renders hydratable HTML
|
|
381
|
-
with fe:b comments and data-fe attributes"]
|
|
382
|
-
B --> C[Browser loads JS bundle]
|
|
383
|
-
C --> D["MyElement.define called
|
|
384
|
-
with template: declarativeTemplate()"]
|
|
385
|
-
D --> E["declarativeTemplate() defines internal f-template
|
|
386
|
-
in the target registry"]
|
|
387
|
-
E --> F["f-template connects to DOM
|
|
388
|
-
→ registers publisher with bridge"]
|
|
389
|
-
F --> G["definition requests template
|
|
390
|
-
via registry + name bridge"]
|
|
391
|
-
G --> H[transformInnerHTML normalises HTML entities]
|
|
392
|
-
H --> I["TemplateParser parses bindings/directives
|
|
393
|
-
builds Schema, strings, values"]
|
|
394
|
-
I --> J["Run definition schema transforms
|
|
395
|
-
attributeMap before observerMap"]
|
|
396
|
-
J --> K["ViewTemplate.create(strings, values)
|
|
397
|
-
returned to definition"]
|
|
398
|
-
K --> L["FASTElementDefinition.define
|
|
399
|
-
registers element with platform"]
|
|
400
|
-
L --> M{"enableHydration() called?"}
|
|
401
|
-
M -- yes --> N["ElementController hydrates existing DOM
|
|
402
|
-
via template.hydrate()"]
|
|
403
|
-
M -- no --> O["ElementController client-side renders
|
|
404
|
-
existing DSD is not reused"]
|
|
405
|
-
N --> P[Interactive component]
|
|
406
|
-
O --> P
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
---
|
|
410
|
-
|
|
411
|
-
## Template Parsing Pipeline
|
|
412
|
-
|
|
413
|
-
The internal `<f-template>` publisher orchestrates the pipeline by creating a
|
|
414
|
-
`TemplateParser` instance and delegating all parsing logic to it. The recursive
|
|
415
|
-
parsing context is encapsulated in a `TemplateResolutionContext` object internal
|
|
416
|
-
to the parser, keeping method signatures lean.
|
|
417
|
-
|
|
418
|
-
### Architecture
|
|
419
|
-
|
|
420
|
-
The parsing pipeline is split across two classes plus definition-scoped
|
|
421
|
-
transforms:
|
|
422
|
-
|
|
423
|
-
- **Internal `<f-template>` publisher** (`template.ts`) — Custom element
|
|
424
|
-
lifecycle, registry/name bridge registration, lifecycle callback dispatch, and
|
|
425
|
-
schema-transform execution. It is a native `HTMLElement`, not a `FASTElement`.
|
|
426
|
-
- **`TemplateParser`** (`template-parser.ts`) — Synchronous template parser:
|
|
427
|
-
converts declarative HTML into `strings`/`values` arrays for
|
|
428
|
-
`ViewTemplate.create()`. Uses a `StringsAccumulator` to track the running
|
|
429
|
-
previous-string in O(1) per binding site instead of O(N) `join("")` calls.
|
|
430
|
-
Independently testable without DOM.
|
|
431
|
-
- **Schema transforms** (`definition-options.ts`) — Extension-provided callbacks run
|
|
432
|
-
after parsing and before `ViewTemplate.create()`. `attributeMap()` runs before
|
|
433
|
-
`observerMap()`.
|
|
434
|
-
|
|
435
|
-
```mermaid
|
|
436
|
-
sequenceDiagram
|
|
437
|
-
participant DOM
|
|
438
|
-
participant FTE as internal f-template
|
|
439
|
-
participant TP as TemplateParser
|
|
440
|
-
participant U as utilities.ts
|
|
441
|
-
participant S as Schema
|
|
442
|
-
participant Transforms as schema transforms
|
|
443
|
-
|
|
444
|
-
DOM->>FTE: connectedCallback()
|
|
445
|
-
FTE->>FTE: register publisher with bridge
|
|
446
|
-
FTE->>FTE: publishTemplate(definition)
|
|
447
|
-
FTE->>S: new Schema(name)
|
|
448
|
-
FTE->>U: transformInnerHTML(this.innerHTML)
|
|
449
|
-
FTE->>TP: parser.parse(innerHTML, schema)
|
|
450
|
-
loop getNextBehavior(innerHTML)
|
|
451
|
-
TP->>U: getNextBehavior()
|
|
452
|
-
U-->>TP: DataBindingBehaviorConfig | TemplateDirectiveBehaviorConfig
|
|
453
|
-
alt content binding
|
|
454
|
-
TP->>TP: resolveContentBinding()
|
|
455
|
-
TP->>TP: resolveAccessBinding()
|
|
456
|
-
TP->>U: bindingResolver()
|
|
457
|
-
U->>S: schema.addPath()
|
|
458
|
-
U-->>TP: (x,c) => value function
|
|
459
|
-
else attribute binding
|
|
460
|
-
TP->>TP: resolveAttributeBinding()
|
|
461
|
-
alt event (@)
|
|
462
|
-
TP->>TP: resolveEventBinding()
|
|
463
|
-
else boolean (?)
|
|
464
|
-
TP->>U: getBooleanBinding() or resolveAccessBinding()
|
|
465
|
-
else default
|
|
466
|
-
TP->>TP: resolveAccessBinding()
|
|
467
|
-
end
|
|
468
|
-
else attributeDirective (slotted / children / ref)
|
|
469
|
-
TP->>TP: resolveAttributeDirectiveBinding()
|
|
470
|
-
else templateDirective (when / repeat)
|
|
471
|
-
TP->>TP: resolveTemplateDirective()
|
|
472
|
-
note over TP: recurses into resolveStringsAndValues
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
TP-->>FTE: { strings, values }
|
|
476
|
-
FTE->>Transforms: attributeMap(), then observerMap()
|
|
477
|
-
FTE->>TP: parser.createTemplate(strings, values)
|
|
478
|
-
FTE-->>DOM: return ViewTemplate to definition resolver
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### TemplateParser method decomposition
|
|
482
|
-
|
|
483
|
-
| Method | Visibility | Role |
|
|
484
|
-
|---|---|---|
|
|
485
|
-
| `parse()` | public | Entry point: parses declarative HTML into `{ strings, values }`. |
|
|
486
|
-
| `createTemplate()` | public | Creates a `ViewTemplate` from resolved strings and values, lazily installing declarative debug messages. |
|
|
487
|
-
| `resolveStringsAndValues()` | private | Creates `strings`/`values` arrays and delegates to `resolveInnerHTML()`. |
|
|
488
|
-
| `resolveInnerHTML()` | private | Recursive HTML parser that dispatches to data binding or template directive handlers. |
|
|
489
|
-
| `resolveDataBinding()` | private | Thin dispatcher that routes to `resolveContentBinding()`, `resolveAttributeBinding()`, or `resolveAttributeDirectiveBinding()`. |
|
|
490
|
-
| `resolveContentBinding()` | private | Handles `{{expression}}` in text content. |
|
|
491
|
-
| `resolveAttributeBinding()` | private | Handles `{{expression}}` in HTML attributes; dispatches to `resolveEventBinding()` or `resolveAccessBinding()` based on aspect. |
|
|
492
|
-
| `resolveAttributeDirectiveBinding()` | private | Handles `f-children`, `f-slotted`, `f-ref` directives. |
|
|
493
|
-
| `resolveAccessBinding()` | private | Shared helper for access-type bindings (content, boolean-attribute fallback, default attribute). |
|
|
494
|
-
| `resolveEventBinding()` | private | Handles event bindings (`@event`), including arg parsing and owner resolution. |
|
|
495
|
-
| `resolveTemplateDirective()` | private | Handles `<f-when>` and `<f-repeat>` directives. |
|
|
496
|
-
| `resolveAttributeDirective()` | private | Creates FAST `children()`, `slotted()`, or `ref()` directives. |
|
|
497
|
-
|
|
498
|
-
### TemplateResolutionContext
|
|
499
|
-
|
|
500
|
-
The `TemplateResolutionContext` interface (internal to `TemplateParser`) groups the stable fields that flow through the recursive parsing pipeline:
|
|
501
|
-
|
|
502
|
-
```typescript
|
|
503
|
-
interface TemplateResolutionContext {
|
|
504
|
-
parentContext: string | null; // Current repeat item alias (e.g. "item")
|
|
505
|
-
level: number; // Nesting depth for repeat directives
|
|
506
|
-
schema: Schema; // JSON schema builder for property tracking
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
`rootPropertyName` is intentionally kept separate because it is selectively mutated per branch and must not leak across sibling binding resolutions.
|
|
511
|
-
|
|
512
|
-
### Key parsing functions (utilities.ts)
|
|
513
|
-
|
|
514
|
-
| Function | Role |
|
|
515
|
-
|---|---|
|
|
516
|
-
| `getNextBehavior(innerHTML)` | Top-level scanner: returns the next `DataBindingBehaviorConfig` or `TemplateDirectiveBehaviorConfig`, or `null` when done. |
|
|
517
|
-
| `getNextDataBindingBehavior(innerHTML)` | Identifies whether the next binding is `{{}}`, `{{{}}}`, or `{}` and classifies it as content/attribute/attributeDirective. |
|
|
518
|
-
| `getNextDirectiveBehavior(innerHTML)` | Finds the next `<f-when>` or `<f-repeat>` and its matching close tag. |
|
|
519
|
-
| `bindingResolver(...)` | Builds a `(x, c) => value` closure for a given path; also calls `schema.addPath()`. |
|
|
520
|
-
| `pathResolver(path, contextPath, level, schema)` | Returns a closure that traverses an object using dot-notation, handling repeat context levels. |
|
|
521
|
-
| `getBooleanBinding(...)` | Returns a `(x, c) => boolean` closure for `<f-when>` expressions. |
|
|
522
|
-
| `assignObservables(schema, rootSchema, data, target, rootProperty)` | Wraps objects/arrays in `Proxy` for deep observation. |
|
|
523
|
-
| `deepMerge(target, source)` | Merges source into an existing proxy, preserving proxy identity and triggering observable notifications. |
|
|
524
|
-
| `transformInnerHTML(html)` | Normalises HTML-encoded operator characters (`>`, `<`, etc.) used in `<f-when>` expressions. |
|
|
525
|
-
|
|
526
|
-
### Binding classification
|
|
527
|
-
|
|
528
|
-
```
|
|
529
|
-
innerHTML token
|
|
530
|
-
├── {{{ ... }}} → unescaped content binding (innerHTML div)
|
|
531
|
-
├── {{ ... }} → default binding
|
|
532
|
-
│ ├── attr="{{expr}}" → attribute binding (aspect: null / ":" / "?")
|
|
533
|
-
│ └── {{expr}} in text → content binding
|
|
534
|
-
└── { ... } → client-side binding
|
|
535
|
-
├── @event="{handler(...)}" → event binding (aspect "@")
|
|
536
|
-
└── f-dir="{prop}" → attribute directive binding
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
---
|
|
540
|
-
|
|
541
|
-
## Schema and Observer Map
|
|
542
|
-
|
|
543
|
-
The `Schema` class accumulates all binding paths discovered during parsing into an instance-level JSON Schema map (`schemaMap`) indexed by `rootPropertyName → JSONSchema`. Each `Schema` instance also registers itself in the module-level `schemaRegistry` (keyed by custom element name) for cross-element `$ref` resolution.
|
|
544
|
-
|
|
545
|
-
```mermaid
|
|
546
|
-
flowchart LR
|
|
547
|
-
A["Template binding\nuser.details.age"] --> B[bindingResolver]
|
|
548
|
-
B --> C[schema.addPath\ntype:'access'\npath:'user.details.age'\nrootProperty:'user']
|
|
549
|
-
C --> D["schema.schemaMap\n{'user' => JSONSchema}"]
|
|
550
|
-
C --> D2["schemaRegistry\n{'my-el' => schemaMap}"]
|
|
551
|
-
D --> E[ObserverMap.defineProperties]
|
|
552
|
-
E --> E1["applyConfigToSchema\nstamps $observe: false on excluded schema nodes"]
|
|
553
|
-
E1 --> F[Observable.defineProperty on prototype\nfor 'user']
|
|
554
|
-
E1 --> G[install instanceResolverChanged handler]
|
|
555
|
-
G --> H["when user= is set:\nassignObservables checks schema.$observe\n→ Proxy wraps observed paths only\ndeep mutations → Observable.notify"]
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
For a deep dive into the schema structure, context tracking, and proxy system
|
|
559
|
-
see
|
|
560
|
-
[DECLARATIVE_SCHEMA_OBSERVER_MAP.md](./DECLARATIVE_SCHEMA_OBSERVER_MAP.md).
|
|
561
|
-
|
|
562
|
-
### `$observe` flag on schema nodes
|
|
563
|
-
|
|
564
|
-
When an `ObserverMapConfig` with a `properties` key is provided, `ObserverMap.defineProperties()` calls `applyConfigToSchema()` to stamp `$observe: false` on excluded schema nodes **before** the proxy system runs. This is a one-time pre-processing pass that walks the config and schema trees in parallel:
|
|
565
|
-
|
|
566
|
-
- `false` in the config → `$observe: false` is stamped recursively on the node and all its descendants.
|
|
567
|
-
- `$observe: false` on a config node → the schema node is stamped, and unlisted children inherit the stamp.
|
|
568
|
-
- `true` in the config → no stamp needed (observed is the default).
|
|
569
|
-
- Config paths not in the schema are silently ignored.
|
|
570
|
-
|
|
571
|
-
**Convention: stamp-only-when-excluding.** The `$observe` flag is only ever set to `false` — it is never explicitly set to `true`. Absence of `$observe` (i.e. `undefined`) means the node is observed. This means:
|
|
572
|
-
|
|
573
|
-
- When Observer Map is enabled without a `properties` tree, `applyConfigToSchema`
|
|
574
|
-
is never called and no schema nodes are mutated — zero overhead for the
|
|
575
|
-
common case.
|
|
576
|
-
- The proxy system uses `isSchemaExcluded(schema)` (checks `$observe === false` with no observed descendants) as the single predicate for all skip/suppress decisions.
|
|
577
|
-
- Schema nodes without `$observe` are always treated as observed.
|
|
578
|
-
|
|
579
|
-
### AttributeMap and leaf bindings
|
|
580
|
-
|
|
581
|
-
When `attributeMap()` is enabled with a declarative template, it registers a
|
|
582
|
-
definition schema transform. With a manually supplied definition schema, it runs
|
|
583
|
-
immediately. The extension constructs an `AttributeMap` implementation and calls
|
|
584
|
-
`defineProperties()`. It iterates `Schema.getRootProperties()` and skips any
|
|
585
|
-
property whose schema entry contains `properties`, `type`, or `anyOf` — keeping
|
|
586
|
-
only plain leaf bindings. For each leaf:
|
|
587
|
-
|
|
588
|
-
1. The schema key is used as the **JS property name**.
|
|
589
|
-
2. The **HTML attribute name** depends on the `attribute-name-strategy`:
|
|
590
|
-
- `"camelCase"` (default): the attribute name is derived by converting the camelCase property name to kebab-case (e.g. `fooBar` → `foo-bar`).
|
|
591
|
-
- `"none"`: the attribute name equals the property name (e.g. `foo-bar` → `foo-bar`).
|
|
592
|
-
3. A new `AttributeDefinition` is registered via `Observable.defineProperty`.
|
|
593
|
-
4. `FASTElementDefinition.attributeLookup` is keyed by the HTML attribute name and `propertyLookup` is keyed by the JS property name so `attributeChangedCallback` can route attribute changes to the correct property.
|
|
594
|
-
|
|
595
|
-
When using the `"none"` strategy, property names may contain dashes and must be
|
|
596
|
-
accessed via bracket notation (e.g. `element["foo-bar"]`). When using
|
|
597
|
-
`"camelCase"`, property names are standard JS identifiers (e.g.
|
|
598
|
-
`element.fooBar`).
|
|
599
|
-
|
|
600
|
-
Schema transforms run in deterministic order. When both extensions are supplied,
|
|
601
|
-
attribute mapping runs before observer mapping.
|
|
602
|
-
|
|
603
|
-
---
|
|
604
|
-
|
|
605
|
-
## Lifecycle
|
|
606
|
-
|
|
607
|
-
```mermaid
|
|
608
|
-
sequenceDiagram
|
|
609
|
-
participant App as Application JS
|
|
610
|
-
participant FER as FASTElementDefinition
|
|
611
|
-
participant FTE as internal f-template
|
|
612
|
-
participant EC as ElementController
|
|
613
|
-
participant PerEl as TemplateLifecycleCallbacks
|
|
614
|
-
participant Global as HydrationOptions
|
|
615
|
-
|
|
616
|
-
App->>Global: enableHydration(globalCallbacks) [optional]
|
|
617
|
-
App->>FER: await MyElement.define({name:'my-el', template: declarativeTemplate(callbacks)}, [attributeMap(), observerMap()])
|
|
618
|
-
note over FER: definition composed; resolver waits for template
|
|
619
|
-
|
|
620
|
-
DOM->>FTE: f-template connected to DOM
|
|
621
|
-
FTE->>FTE: bridge matches registry + name
|
|
622
|
-
FTE->>PerEl: elementDidRegister('my-el')
|
|
623
|
-
FTE->>PerEl: templateWillUpdate('my-el')
|
|
624
|
-
FTE->>FTE: parse template → schema → transforms → ViewTemplate
|
|
625
|
-
FTE->>FER: return viewTemplate to resolver
|
|
626
|
-
FER->>PerEl: templateDidUpdate('my-el')
|
|
627
|
-
FER->>FER: customElements.define('my-el', MyElement)
|
|
628
|
-
FER->>PerEl: elementDidDefine('my-el')
|
|
629
|
-
|
|
630
|
-
DOM->>EC: element instance connects with existing shadow root
|
|
631
|
-
EC->>EC: isPrerendered = true (existing shadow root detected)
|
|
632
|
-
alt enableHydration() was called
|
|
633
|
-
EC->>Global: hydrationStarted()
|
|
634
|
-
EC->>PerEl: elementWillHydrate(element)
|
|
635
|
-
EC->>EC: template.hydrate() — maps existing DOM to binding targets
|
|
636
|
-
EC->>PerEl: elementDidHydrate(element)
|
|
637
|
-
EC->>Global: hydrationComplete()
|
|
638
|
-
else hydration not enabled
|
|
639
|
-
EC->>EC: client-side render; isHydrated = false
|
|
640
|
-
end
|
|
641
|
-
```
|
|
642
|
-
|
|
643
|
-
### Lifecycle callback reference
|
|
644
|
-
|
|
645
|
-
| Callback | API | When |
|
|
646
|
-
|---|---|---|
|
|
647
|
-
| `elementDidRegister(name)` | `declarativeTemplate(callbacks)` | The matching `<f-template>` begins publishing for the definition. |
|
|
648
|
-
| `templateWillUpdate(name)` | `declarativeTemplate(callbacks)` | Just before template HTML is parsed. |
|
|
649
|
-
| `templateDidUpdate(name)` | `declarativeTemplate(callbacks)` | After `ViewTemplate` is assigned to the definition. |
|
|
650
|
-
| `elementDidDefine(name)` | `declarativeTemplate(callbacks)` | After platform registration completes. |
|
|
651
|
-
| `elementWillHydrate(source)` | `declarativeTemplate(callbacks)` | Before `ElementController` hydrates a prerendered instance; only after `enableHydration()`. |
|
|
652
|
-
| `elementDidHydrate(source)` | `declarativeTemplate(callbacks)` | After an instance is fully hydrated; only after `enableHydration()`. |
|
|
653
|
-
| `hydrationStarted()` | `enableHydration(options)` | Once, when the first prerendered element begins hydrating. |
|
|
654
|
-
| `hydrationComplete()` | `enableHydration(options)` | Once, after all prerendered elements have completed hydration. |
|
|
655
|
-
|
|
656
|
-
For usage examples see
|
|
657
|
-
[DECLARATIVE_RENDERING_LIFECYCLE.md](./DECLARATIVE_RENDERING_LIFECYCLE.md).
|
|
658
|
-
|
|
659
|
-
---
|
|
660
|
-
|
|
661
|
-
## Integration with fast-element
|
|
662
|
-
|
|
663
|
-
The declarative runtime is a thin orchestration layer on top of
|
|
664
|
-
`@microsoft/fast-element`. It does not re-implement any reactive primitives; it
|
|
665
|
-
converts declarative HTML syntax into the same data structures that `html`
|
|
666
|
-
tagged templates produce.
|
|
667
|
-
|
|
668
|
-
| fast-element primitive | How the declarative runtime uses it |
|
|
669
|
-
|---|---|
|
|
670
|
-
| `FASTElement` | Base class for user components; the internal `<f-template>` publisher is a native `HTMLElement` |
|
|
671
|
-
| `FASTElementDefinition.register()` / template resolvers | Deferred element registration — element waits for its template |
|
|
672
|
-
| `FASTElementDefinition.schema` | Optional schema used by schema-driven extensions; assigned automatically by `declarativeTemplate()` and available for manual schemas |
|
|
673
|
-
| `FASTElementExtension` | Extension callback mechanism used by `attributeMap()` and `observerMap()` to attach schema transforms before template resolution or consume manually supplied schemas |
|
|
674
|
-
| `ViewTemplate.create(strings, values)` | Compiles the resolved strings/values arrays into a `ViewTemplate` |
|
|
675
|
-
| `ElementController` | Automatically detects prerendered content (`isPrerendered`) and hydrates server-rendered DOM using `fe-b` comment/dataset markers via `template.hydrate()` |
|
|
676
|
-
| `Observable.defineProperty()` | Defines observable root properties on element prototypes (ObserverMap) |
|
|
677
|
-
| `Observable.getNotifier()` | Triggers change notifications from proxy handlers |
|
|
678
|
-
| `when(expr, template)` | FAST directive used for `<f-when>` |
|
|
679
|
-
| `repeat(expr, template)` | FAST directive used for `<f-repeat>` |
|
|
680
|
-
| `slotted(options)` | FAST directive used for `f-slotted` |
|
|
681
|
-
| `children(prop)` | FAST directive used for `f-children` |
|
|
682
|
-
| `ref(prop)` | FAST directive used for `f-ref` |
|
|
683
|
-
|
|
684
|
-
### Deferred template attachment via define
|
|
685
|
-
|
|
686
|
-
Standard `FASTElement.define()` returns a `Promise` that resolves immediately when a concrete template is provided at definition time. When `template: declarativeTemplate()` is used, the `Promise` resolves after the matching `<f-template>` supplies a concrete template through the bridge. This unified API replaces the previous `defineAsync()` / `composeAsync()` methods.
|
|
687
|
-
|
|
688
|
-
---
|
|
689
|
-
|
|
690
|
-
## Hydration Model
|
|
691
|
-
|
|
692
|
-
When declarative templates are used, the server must render:
|
|
693
|
-
|
|
694
|
-
1. The custom element tag with its attributes and initial state.
|
|
695
|
-
2. A `<template shadowrootmode="open">` containing pre-rendered HTML annotated with FAST's hydration markers.
|
|
696
|
-
3. An `<f-template>` element somewhere in the page that carries the template definition.
|
|
697
|
-
|
|
698
|
-
With `declarativeTemplate()`, connection gating happens before platform registration: the resolver waits for the matching `<f-template>` and keeps the definition concrete before elements can connect. Hydration can therefore start immediately when `ElementController.connect()` runs. The `defer-hydration` and `needs-hydration` attributes are no longer needed in server-rendered markup.
|
|
699
|
-
|
|
700
|
-
### Hydration marker formats
|
|
701
|
-
|
|
702
|
-
**Content bindings** use HTML comments (data-free, matched by string equality):
|
|
703
|
-
|
|
704
|
-
```
|
|
705
|
-
<!--fe:b-->
|
|
706
|
-
<!--fe:/b-->
|
|
707
|
-
```
|
|
708
|
-
|
|
709
|
-
**Attribute bindings** use a single `data-fe` dataset attribute with binding count:
|
|
710
|
-
|
|
711
|
-
```html
|
|
712
|
-
<el data-fe="3">
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
**Repeat directives** wrap each item in comment pairs:
|
|
716
|
-
```
|
|
717
|
-
<!--fe:r-->
|
|
718
|
-
...item DOM...
|
|
719
|
-
<!--fe:/r-->
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
For detailed examples see
|
|
723
|
-
[DECLARATIVE_RENDERING.md](./DECLARATIVE_RENDERING.md).
|
|
724
|
-
|
|
725
|
-
---
|
|
726
|
-
|
|
727
|
-
## Testing Architecture
|
|
728
|
-
|
|
729
|
-
Each feature is verified by a **Playwright** integration test against a live
|
|
730
|
-
Vite dev server. The `test/declarative/fixtures/` directory contains one
|
|
731
|
-
subdirectory per feature:
|
|
732
|
-
|
|
733
|
-
```
|
|
734
|
-
test/declarative/fixtures/<feature>/
|
|
735
|
-
├── <feature>.spec.ts # Playwright test
|
|
736
|
-
├── entry.html # Entry template with root custom elements
|
|
737
|
-
├── fast-build.config.json # Build configuration for @microsoft/fast-build
|
|
738
|
-
├── index.html # Pre-rendered page (GENERATED by scripts/declarative/build-fixtures.js — do not edit)
|
|
739
|
-
├── main.ts # Component definitions, declarativeTemplate(), extensions, and enableHydration() setup
|
|
740
|
-
├── state.json # Initial state for server-side rendering
|
|
741
|
-
└── templates.html # Declarative <f-template> definitions
|
|
742
|
-
```
|
|
743
|
-
|
|
744
|
-
Fixtures are auto-discovered by scanning for directories that contain `entry.html`, `templates.html`, `state.json`, and `fast-build.config.json`. Both the build script and the Vite config pick up new fixtures automatically — no registration step is needed.
|
|
745
|
-
|
|
746
|
-
For fixtures that use SSR-style pre-rendered HTML,
|
|
747
|
-
`scripts/declarative/build-fixtures.js` invokes `@microsoft/fast-build` with
|
|
748
|
-
`--config` pointing to each fixture's `fast-build.config.json` to generate
|
|
749
|
-
`index.html` from the configured source files.
|
|
750
|
-
|
|
751
|
-
### WebUI Integration Tests
|
|
752
|
-
|
|
753
|
-
A separate integration test suite validates that `@microsoft/webui` can build and render the same fixture templates that `@microsoft/fast-build` processes. This is split into two steps:
|
|
754
|
-
|
|
755
|
-
1. **Build** (`npm run build:fixtures:webui`) — runs
|
|
756
|
-
`scripts/declarative/build-fixtures-with-webui.js`, which extracts
|
|
757
|
-
`<f-template>` elements, builds each fixture with `webui build --plugin=fast`,
|
|
758
|
-
renders the protocol with `state.json`, and writes the output alongside
|
|
759
|
-
`main.ts` and assets to `temp/integrations/webui/fixtures/`.
|
|
760
|
-
2. **Test** (`npm run test:webui-integration`) — builds the fixtures, then runs
|
|
761
|
-
the same Playwright specs against the webui-rendered output served by a Vite
|
|
762
|
-
dev server on port 5174 (configured in
|
|
763
|
-
`playwright.declarative.webui.config.ts`).
|
|
764
|
-
|
|
765
|
-
Run locally with `npm run test:webui-integration` or via the `ci-webui-integration.yml` GitHub Action on PRs and pushes to `main`.
|
|
766
|
-
|
|
767
|
-
#### Skipped tests
|
|
768
|
-
|
|
769
|
-
Some tests are conditionally skipped when running under the webui integration
|
|
770
|
-
config. The `playwright.declarative.webui.config.ts` file sets
|
|
771
|
-
`process.env.FAST_WEBUI_INTEGRATION = "true"`, and individual tests check this
|
|
772
|
-
variable with `test.skip()` to opt out of cases that exercise known differences
|
|
773
|
-
between `fast-build` and `webui` rendering:
|
|
774
|
-
|
|
775
|
-
- **`errors.spec.ts` — "throws an error when no template element is present"**: webui does not render `<f-template>` elements that lack a `<template>` child, so the expected error is never thrown.
|
|
776
|
-
|
|
777
|
-
### Hydration readiness
|
|
778
|
-
|
|
779
|
-
Fixtures that exercise prerendered output wait for hydration to complete before
|
|
780
|
-
running assertions. Each `main.ts` calls `enableHydration({
|
|
781
|
-
hydrationComplete() { ... } })` to set a global flag, and each spec file calls
|
|
782
|
-
`page.waitForFunction()` after `page.goto()` to block until the flag is set.
|
|
783
|
-
See [test/declarative/fixtures/README.md](./test/declarative/fixtures/README.md)
|
|
784
|
-
for the implementation pattern.
|
|
785
|
-
|
|
786
|
-
See
|
|
787
|
-
[test/declarative/fixtures/WRITING_FIXTURES.md](./test/declarative/fixtures/WRITING_FIXTURES.md)
|
|
788
|
-
for the complete fixture authoring guide,
|
|
789
|
-
[test/declarative/fixtures/README.md](./test/declarative/fixtures/README.md)
|
|
790
|
-
for a quick reference, and
|
|
791
|
-
[test/declarative/fixtures/extensions/observer-map-deep-merge/README.md](./test/declarative/fixtures/extensions/observer-map-deep-merge/README.md)
|
|
792
|
-
for an example of a complex multi-feature fixture.
|
|
793
|
-
|
|
794
|
-
---
|
|
795
|
-
|
|
796
|
-
## Further Reading
|
|
797
|
-
|
|
798
|
-
| Document | Topic |
|
|
799
|
-
|---|---|
|
|
800
|
-
| [DECLARATIVE_HTML.md](./DECLARATIVE_HTML.md) | Installation, syntax reference, lifecycle callbacks, usage examples |
|
|
801
|
-
| [DECLARATIVE_RENDERING.md](./DECLARATIVE_RENDERING.md) | Hydratable HTML format: comment markers, dataset attributes, directive markers |
|
|
802
|
-
| [DECLARATIVE_RENDERING_LIFECYCLE.md](./DECLARATIVE_RENDERING_LIFECYCLE.md) | Phase-by-phase rendering lifecycle, callback ordering, performance notes |
|
|
803
|
-
| [DECLARATIVE_SCHEMA_OBSERVER_MAP.md](./DECLARATIVE_SCHEMA_OBSERVER_MAP.md) | Deep dive into Schema JSON structure, ObserverMap proxy system, debugging |
|
|
804
|
-
| [test/declarative/fixtures/README.md](./test/declarative/fixtures/README.md) | Quick reference for fixture structure |
|
|
805
|
-
| [test/declarative/fixtures/WRITING_FIXTURES.md](./test/declarative/fixtures/WRITING_FIXTURES.md) | Complete guide to writing new Playwright fixture tests |
|
|
806
|
-
| [test/declarative/fixtures/extensions/observer-map-deep-merge/README.md](./test/declarative/fixtures/extensions/observer-map-deep-merge/README.md) | Complex deep-merge fixture: observable arrays, nested repeats, conditionals |
|