You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor: modularize Schema, ObserverMap, and AttributeMap in fast-html (#7478)
# Pull Request
## 📖 Description
Modularize Schema, ObserverMap, and AttributeMap in `@microsoft/fast-html` to reduce tight coupling with TemplateElement. This is a **breaking change**.
**Key changes:**
- Moved `ObserverMapConfig`, `ObserverMapPathEntry`, `ObserverMapPathNode`, and `ObserverMapOption` from `template.ts` to `observer-map.ts`
- Moved `AttributeMapConfig` and `AttributeMapOption` from `template.ts` to `attribute-map.ts`
- Replaced `Schema.jsonSchemaMap` (static property) with an instance-level `schemaMap` and a module-level `schemaRegistry` export for cross-element `$ref` resolution
- Added `Schema`, `AttributeMap`, `schemaRegistry`, `JSONSchema`, and `CachedPathMap` to the public API exports
- Reversed the dependency direction: `template.ts` now imports from `observer-map.ts` and `attribute-map.ts`, instead of the other way around
## 👩💻 Reviewer Notes
The module dependency direction is now:
```
template.ts ──imports──▶ observer-map.ts (config types)
template.ts ──imports──▶ attribute-map.ts (config types)
template.ts ──imports──▶ schema.ts
observer-map.ts ──imports──▶ schema.ts (types only)
attribute-map.ts ──imports──▶ schema.ts (types only)
utilities.ts ──imports──▶ schema.ts (schemaRegistry for cross-element $ref)
```
## 📑 Test Plan
All 873 existing tests pass across Chromium, Firefox, and WebKit. No new tests were needed as the refactor preserves all existing behavior — only the internal module boundaries and export surface changed.
## ✅ Checklist
### General
- [x] I have included a change request file using `$ npm run change`
- [ ] I have added tests for my changes.
- [x] I have tested my changes.
- [x] I have updated the project documentation to reflect my changes.
- [x] I have read the [CONTRIBUTING](https://github.com/microsoft/fast/blob/main/CONTRIBUTING.md) documentation and followed the [standards](https://github.com/microsoft/fast/blob/main/CODE_OF_CONDUCT.md#our-standards) for this project.
utilities.ts ──imports──▶ schema.ts (schemaRegistry for cross-element $ref resolution)
192
+
```
193
+
179
194
---
180
195
181
196
## Exports and Public API
@@ -184,28 +199,40 @@ packages/fast-html/
184
199
import {
185
200
TemplateElement,
186
201
TemplateParser,
202
+
Schema,
203
+
schemaRegistry,
187
204
ObserverMap,
205
+
AttributeMap,
188
206
typeObserverMapConfig,
189
207
typeObserverMapPathEntry,
190
208
typeObserverMapPathNode,
209
+
typeAttributeMapConfig,
210
+
typeJSONSchema,
211
+
typeCachedPathMap,
191
212
} from"@microsoft/fast-html";
192
213
```
193
214
194
-
Three primary exports are intended for application code:
215
+
Primary exports intended for application code:
195
216
196
217
| Export | Purpose |
197
218
|---|---|
198
219
|`TemplateElement`| Define the `<f-template>` element; configure callbacks and per-element options. |
199
220
|`TemplateParser`| Standalone parser that converts declarative HTML into `ViewTemplate` strings/values. Can be used independently of `<f-template>` for programmatic template compilation. |
200
-
|`ObserverMap`| Advanced: access the observer-map class directly if building tooling. |
221
+
|`Schema`| 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. |
222
+
|`schemaRegistry`| 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). |
223
+
|`ObserverMap`| Automatic observable setup using the schema; defines observable properties and installs proxy-based deep change tracking. Configuration types (`ObserverMapConfig`, `ObserverMapPathEntry`, `ObserverMapPathNode`) are co-located in this module. |
224
+
|`AttributeMap`| Automatic `@attr` property registration for leaf bindings in the template. Configuration type (`AttributeMapConfig`) is co-located in this module. |
201
225
202
-
Additionally, the following types are exported for use in `observerMap` configuration:
226
+
Additionally, the following types are exported:
203
227
204
-
| Type | Purpose |
205
-
|---|---|
206
-
|`ObserverMapConfig`| Configuration object for the `observerMap` option; accepts optional `properties` key. |
207
-
|`ObserverMapPathEntry`|`boolean \| ObserverMapPathNode` — a node in the observation path tree. |
208
-
|`ObserverMapPathNode`| Object node with optional `$observe` and child property overrides. |
228
+
| Type | Source Module | Purpose |
229
+
|---|---|---|
230
+
|`ObserverMapConfig`|`observer-map.ts`| Configuration object for the `observerMap` option; accepts optional `properties` key. |
231
+
|`ObserverMapPathEntry`|`observer-map.ts`|`boolean \| ObserverMapPathNode` — a node in the observation path tree. |
232
+
|`ObserverMapPathNode`|`observer-map.ts`| Object node with optional `$observe` and child property overrides. |
233
+
|`AttributeMapConfig`|`attribute-map.ts`| Configuration object for the `attributeMap` option; accepts `attribute-name-strategy`. |
234
+
|`JSONSchema`|`schema.ts`| JSON Schema interface used by `Schema` for property structure. |
235
+
|`CachedPathMap`|`schema.ts`|`Map<string, Map<string, JSONSchema>>` — the shape of the schema registry. |
209
236
210
237
---
211
238
@@ -377,13 +404,14 @@ innerHTML token
377
404
378
405
## Schema and Observer Map
379
406
380
-
The `Schema` class accumulates all binding paths discovered during parsing into a static JSON Schema map indexed by `customElementName → rootPropertyName → JSONSchema`.
407
+
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.
Copy file name to clipboardExpand all lines: packages/fast-html/SCHEMA_OBSERVER_MAP.md
+13-13Lines changed: 13 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,7 +58,7 @@ The `Schema` class is responsible for building JSON Schema definitions that map
58
58
```typescript
59
59
constructor(name: string)
60
60
```
61
-
Creates a new schema instance for a specific custom element name and initializes an entry in the static `jsonSchemaMap`.
61
+
Creates a new schema instance for a specific custom element name and initializes an instance-level `schemaMap`. The instance also registers itself in the module-level `schemaRegistry` for cross-element `$ref` resolution.
62
62
63
63
#### addPath
64
64
```typescript
@@ -131,7 +131,7 @@ Creates an observer map instance that will configure the provided class prototyp
131
131
publicdefineProperties(): void
132
132
```
133
133
The main method that:
134
-
1. Iterates through all root properties defined in the schema (each custom element in the jsonSchemaMap contains multiple schemas, one for each root property)
134
+
1. Iterates through all root properties defined in the schema (each schema instance contains multiple root property schemas)
135
135
2. Defines observable properties using FAST Element's `Observable.defineProperty` (an alternative to the `@observable` decorator syntax used in custom element classes)
136
136
3. Sets up property change handlers that create proxies for nested objects
137
137
@@ -375,16 +375,18 @@ This creates nested context definitions where the `post` context understands its
**RationaleforModule-levelRegistry**: Theregistryallowscross-element`$ref`resolutionfornestedcomponentsinsidef-templates. Whenanobjectorarrayispassedtoanothercustomelementwithinanf-template, thatnestedcomponentneedstoobservetheentirerootproperty's structure based on the binding paths within that nested component. The registry allows all components to access and contribute to the same schema definitions, ensuring consistent observation behavior across component boundaries.
388
390
389
391
### ContextTracking
390
392
@@ -408,15 +410,13 @@ The schema system tracks binding contexts using special metadata:
0 commit comments