diff --git a/change/@microsoft-fast-element-531ef150-b9bf-4195-aca1-bad21bca8deb.json b/change/@microsoft-fast-element-531ef150-b9bf-4195-aca1-bad21bca8deb.json new file mode 100644 index 00000000000..e1834492341 --- /dev/null +++ b/change/@microsoft-fast-element-531ef150-b9bf-4195-aca1-bad21bca8deb.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Update documentation around the necessity for await when using define", + "packageName": "@microsoft/fast-element", + "email": "7559015+janechu@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/docs/declarative/design.md b/packages/fast-element/docs/declarative/design.md index 4772cff5f36..c03fdbccb52 100644 --- a/packages/fast-element/docs/declarative/design.md +++ b/packages/fast-element/docs/declarative/design.md @@ -622,7 +622,7 @@ sequenceDiagram participant Hydration as enableHydration().whenHydrated() App->>Hydration: const hydration = enableHydration() [optional] - App->>FER: await MyElement.define({name:'my-el', template: declarativeTemplate()}, [attributeMap(), observerMap()]) + App->>FER: MyElement.define({name:'my-el', template: declarativeTemplate()}, [attributeMap(), observerMap()]) note over FER: definition composed; resolver waits for template DOM->>FTE: f-template connected to DOM diff --git a/packages/fast-element/docs/declarative/lifecycle.md b/packages/fast-element/docs/declarative/lifecycle.md index bc8971c161c..310c1c89be6 100644 --- a/packages/fast-element/docs/declarative/lifecycle.md +++ b/packages/fast-element/docs/declarative/lifecycle.md @@ -50,7 +50,7 @@ class MyComponent extends FASTElement { } // Register with the declarative template bridge -await MyComponent.define({ +MyComponent.define({ name: "my-component", template: declarativeTemplate(), }); diff --git a/packages/fast-element/docs/migration/fast-element-3.md b/packages/fast-element/docs/migration/fast-element-3.md index 3efc60895fd..07203fae252 100644 --- a/packages/fast-element/docs/migration/fast-element-3.md +++ b/packages/fast-element/docs/migration/fast-element-3.md @@ -383,17 +383,17 @@ This is a **breaking change** for SSR output format. Any system that produces or | Removed | Replacement | |---|---| -| `FASTElement.defineAsync()` | Subclass `define()` calls (now return `Promise`) | +| `FASTElement.defineAsync()` | Subclass `define()` calls | | `FASTElement.compose()` and subclass `compose()` calls | Subclass `define()` calls | | `FASTElementDefinition.composeAsync()` | Subclass `define()` calls | | `FASTElementDefinition.registerAsync()` | `FASTElementDefinition.register()` (same `Promise` return type) | ### Changed behavior -- Subclass **`define()`** calls now return `Promise`. When a concrete - template is provided at definition time, the Promise resolves immediately. - When `template: declarativeTemplate()` is used, the Promise resolves after - the matching `` supplies the concrete template. +- Subclass **`define()`** calls return `Promise` only for code that + explicitly needs to observe registration completion. When + `template: declarativeTemplate()` is used, that Promise resolves after the + matching `` supplies the concrete template. - Subclass compose helpers are no longer part of the public authoring surface; use subclass `define()` for registration. - **`@customElement` decorator** calls `define()` internally but does not return the Promise (fire-and-forget). For complete definitions with a template, the element is registered via a microtask. @@ -409,7 +409,7 @@ This is a **breaking change** for SSR output format. Any system that produces or }); // After - await MyElement.define({ + MyElement.define({ name: "my-element", template: declarativeTemplate(), }); @@ -427,7 +427,7 @@ This is a **breaking change** for SSR output format. Any system that produces or }).define(); // After - await MyElement.define({ + MyElement.define({ name: "my-element", template, styles, @@ -446,7 +446,7 @@ This is a **breaking change** for SSR output format. Any system that produces or })).define(); // After - await MyElement.define({ + MyElement.define({ name: "my-element", template, styles, @@ -471,7 +471,7 @@ This is a **breaking change** for SSR output format. Any system that produces or FASTElementDefinition.compose(MyElement, options).define(); // After - await MyElement.define(options); + MyElement.define(options); ``` ## Dynamic stylesheet behaviors (v3) diff --git a/packages/fast-element/docs/migration/fast-html.md b/packages/fast-element/docs/migration/fast-html.md index d1676ed38bd..10c6cc92bfb 100644 --- a/packages/fast-element/docs/migration/fast-html.md +++ b/packages/fast-element/docs/migration/fast-html.md @@ -36,9 +36,9 @@ See the [`@microsoft/fast-element` migration guide](./fast-element-3.md#hydratio ### Migration steps 1. Replace `RenderableFASTElement(MyComponent).defineAsync({...})` with - `await MyComponent.define({...})` and use `declarativeTemplate()` for - declarative templates. `declarativeTemplate()` is the waiting behavior: it - resolves the matching `` before `define()` completes. + `MyComponent.define({...})` and use `declarativeTemplate()` for declarative + templates. If code explicitly observes the Promise returned by `define()`, + it resolves after the matching `` supplies the concrete template. ```typescript // Before @@ -49,7 +49,7 @@ See the [`@microsoft/fast-element` migration guide](./fast-element-3.md#hydratio }); // After - await MyComponent.define({ + MyComponent.define({ name: "my-component", template: declarativeTemplate(), }); diff --git a/sites/website/src/docs/3.x/declarative-templates/server-rendering.md b/sites/website/src/docs/3.x/declarative-templates/server-rendering.md index e53d2ae8fa8..b9766750f5b 100644 --- a/sites/website/src/docs/3.x/declarative-templates/server-rendering.md +++ b/sites/website/src/docs/3.x/declarative-templates/server-rendering.md @@ -422,8 +422,8 @@ offending marker. `declarativeTemplate()` waits for one connected `` whose `name` matches the element definition. Template-first and definition-first loading both -work, but the custom element definition does not finish until the matching -template is available. +work, but custom element registration completes only after the matching template +is available. ```html @@ -434,7 +434,7 @@ template is available. ```ts class UserCard extends FASTElement {} -await UserCard.define({ +UserCard.define({ name: "user-card", template: declarativeTemplate(), }); diff --git a/sites/website/src/docs/3.x/migration-guide.md b/sites/website/src/docs/3.x/migration-guide.md index c9b26cd8ea7..37285d5bbac 100644 --- a/sites/website/src/docs/3.x/migration-guide.md +++ b/sites/website/src/docs/3.x/migration-guide.md @@ -1,579 +1,10 @@ --- -id: migration-guide -title: Migration Guide layout: 3x eleventyNavigation: key: migration-guide3x title: Migration Guide + order: 8 navigationOptions: activeKey: migration-guide3x -keywords: - - migrate - - migration - - web components +permalink: false --- - -# Migrating from 2.x to 3.x - -FAST Element 3.x targets client-side browser `Window` runtimes. Do not import or -run the FAST Element client runtime in workers, server-side JavaScript runtimes, -or other non-window hosts. Server rendering tools may emit HTML, but hydration -and client rendering run in the browser window where DOM, Custom Elements, -Shadow DOM, and `requestAnimationFrame` are available. - -## Breaking Changes - -### Browser `Window` runtime and native globals required - -FAST Element v3 no longer installs a `globalThis` polyfill and no longer -installs or depends on a `requestIdleCallback` / `cancelIdleCallback` fallback. -Load a `globalThis` polyfill before importing FAST only if you still support an -older browser that lacks it. Do not run the FAST Element client runtime in -workers or server-side JavaScript hosts. - -See the [package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md) -for the complete native globals and scheduler migration steps. - -### `HydratableElementController` removed - -The `HydratableElementController` class has been removed. Its functionality is now built into `ElementController` via automatic prerendered content detection. When a component connects with an existing shadow root (from SSR or declarative shadow DOM), the system automatically hydrates the existing DOM. - -2.x Example: -```ts -import { HydratableElementController } from "@microsoft/fast-element"; - -HydratableElementController.install(); -``` - -3.x: Hydration is now opt-in via `enableHydration()`: - -```ts -import { enableHydration } from "@microsoft/fast-element/hydration.js"; - -enableHydration(); -``` - -Remove any `import "@microsoft/fast-element/install-hydration.js"` side-effect -imports as part of the migration. The older -`install-hydratable-view-templates.js` side-effect helper should also be -removed; `enableHydration()` installs hydratable template support. -`@microsoft/fast-element` no longer installs hydration automatically. - -### `needs-hydration` and `defer-hydration` markup no longer required - -These attributes are no longer needed in server-rendered markup. - -2.x Markup: -```html - - - -``` - -3.x Markup: -```html - - - -``` - -### `HydrationControllerCallbacks` replaced by hydration promises - -2.x Example: -```ts -import { HydratableElementController } from "@microsoft/fast-element"; - -HydratableElementController.config({ - /* hydration callbacks */ -}); -``` - -3.x Example: -```ts -import { enableHydration } from "@microsoft/fast-element/hydration.js"; - -const hydration = enableHydration(); -await hydration.whenHydrated(); -``` - -Use `enableHydration().whenHydrated("my-element")` when application code needs to -wait for hydration work associated with a specific tag name. Use -`enableHydration().whenHydrated()` for the active global hydration batch. When -`stopHydration: StopHydration.never` is configured, the global promise -intentionally remains pending because hydration never reaches a global -completion point. - -### `isPrerendered` and `isHydrated` split - -The `isPrerendered` property on `ElementController` and `ViewController` now resolves `true` when the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran. A new `isHydrated: Promise` property resolves `true` only when hydration actually ran successfully. - -```ts -connectedCallback() { - super.connectedCallback(); - const ctrl = this.$fastController; - const prerendered = await ctrl.isPrerendered; - const hydrated = await ctrl.isHydrated; - - if (prerendered && !hydrated) { - // Had DSD but hydration wasn't enabled - } -} -``` - -### Declarative HTML imports moved - -The `@microsoft/fast-html` package has been removed. Import declarative HTML -APIs from FAST Element path exports instead: - -```ts -// Before -import { TemplateElement } from "@microsoft/fast-html"; -import { deepMerge } from "@microsoft/fast-html/utilities.js"; - -// After -import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; -import { deepMerge } from "@microsoft/fast-element/declarative-utilities.js"; -``` - -Import FAST Element APIs such as `FASTElement`, `FAST`, `ElementController`, -definition/controller types, and helper APIs from `@microsoft/fast-element`: - -| API | Import path | -|---|---| -| `Updates` | `@microsoft/fast-element` | -| `Observable`, `observable` | `@microsoft/fast-element` | -| `attr`, `AttributeDefinition`, converters, `ValueConverter` | `@microsoft/fast-element` | -| `Binding`, `oneWay`, `oneTime`, `listener` | `@microsoft/fast-element` | -| `DOM`, `DOMAspect`, `DOMPolicy` | `@microsoft/fast-element` | -| `Schema`, `schemaRegistry`, schema types | `@microsoft/fast-element` | -| `css` | `@microsoft/fast-element` | -| `html`, `ViewTemplate`, `HTMLView` | `@microsoft/fast-element` | -| `Compiler`, `HTMLDirective`, `htmlDirective`, templating/view types | `@microsoft/fast-element` | -| `render`, `RenderBehavior`, `RenderDirective` | `@microsoft/fast-element` | -| `enableHydration`, `deferHydrationAttribute`, `HydrationTracker`, hydration types | `@microsoft/fast-element/hydration.js` | -| `fastElementRegistry` | `@microsoft/fast-element/registry.js` | -| `ArrayObserver` | `@microsoft/fast-element` | -| `volatile` | `@microsoft/fast-element` | -| `children` | `@microsoft/fast-element` | -| `elements`, `NodeObservationDirective` | `@microsoft/fast-element` | -| `ref` | `@microsoft/fast-element` | -| `slotted` | `@microsoft/fast-element` | -| `when` | `@microsoft/fast-element` | -| `repeat` | `@microsoft/fast-element` | - -### `FASTElementDefinition.isRegistered` removed - -The static `FASTElementDefinition.isRegistered` map is no longer public. Import -`fastElementRegistry` from `@microsoft/fast-element/registry.js` instead: - -```ts -import { fastElementRegistry } from "@microsoft/fast-element/registry.js"; - -const definition = await fastElementRegistry.whenRegistered("my-element"); -``` - -Use `fastElementRegistry.getByType(type)` or -`fastElementRegistry.getForInstance(instance)` when you already have a -constructor or element instance. - -Hydration is opt-in via `enableHydration()` from `@microsoft/fast-element/hydration.js`. - -### Optional helper imports moved to flat path exports - -FAST Element v3 adds focused flat path exports for optional helpers and removes -older nested helper paths. - -| Before | After | -|---|---| -| `@microsoft/fast-element/binding/two-way.js` | `@microsoft/fast-element/two-way.js` | -| `@microsoft/fast-element/binding/signal.js` | `@microsoft/fast-element/signal.js` | -| Deep directive imports | `@microsoft/fast-element/children.js`, `@microsoft/fast-element/repeat.js`, `@microsoft/fast-element/when.js`, `@microsoft/fast-element/ref.js`, `@microsoft/fast-element/slotted.js`, or `@microsoft/fast-element/node-observation.js` | -| Declarative map helpers | `@microsoft/fast-element/attribute-map.js` and `@microsoft/fast-element/observer-map.js` | - -Remove imports from deleted package exports such as -`@microsoft/fast-element/metadata.js`, `@microsoft/fast-element/testing.js`, and -`@microsoft/fast-element/pending-task.js`. Replace hydration side-effect imports -with `enableHydration()` from `@microsoft/fast-element/hydration.js`. - -See the [Path Exports](./advanced/path-exports.md) reference and the -[package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md) -for the full import migration table. - -### `attribute-name-strategy` now defaults to `camelCase` - -Declarative attribute mapping now defaults to `"camelCase"` instead of -`"none"`. With the default strategy, `{{firstName}}` maps to the `firstName` -property and the `first-name` HTML attribute. If you relied on v2 literal -attribute names, configure both the client and server to use `"none"`: - -```ts -import { attributeMap } from "@microsoft/fast-element/attribute-map.js"; - -MyElement.define( - { - name: "my-element", - template: declarativeTemplate(), - }, - [attributeMap({ "attribute-name-strategy": "none" })], -); -``` - -```bash -fast build --attribute-name-strategy=none -``` - -### Declarative `TemplateElement` API removed - -The `` implementation is now internal and is defined automatically -when an element uses `template: declarativeTemplate()`. - -| Removed | Replacement | -|---|---| -| `TemplateElement` public export | `declarativeTemplate()` | -| `TemplateElement.define({ name: "f-template" })` | No manual definition needed | -| `TemplateElement.config(callbacks)` | `enableHydration().whenHydrated(tagName)` for tag-specific hydration waits and `enableHydration().whenHydrated()` for the active hydration batch | -| `TemplateElement.options(...)` | `attributeMap()` and `observerMap()` define extensions | -| `AttributeMap` / `ObserverMap` class exports from the old declarative public surface | `attributeMap()` / `observerMap()` helpers and config types | - -Import `declarativeTemplate()` and schema-driven map extensions from their -focused path exports: - -```ts -import { attributeMap } from "@microsoft/fast-element/attribute-map.js"; -import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; -import { enableHydration } from "@microsoft/fast-element/hydration.js"; -import { observerMap } from "@microsoft/fast-element/observer-map.js"; - -const hydration = enableHydration(); - -await MyElement.define( - { - name: "my-element", - template: declarativeTemplate(), - }, - [attributeMap(), observerMap()], -); - -await hydration.whenHydrated("my-element"); -await hydration.whenHydrated(); -``` - -`FASTElementDefinition.schema` is optional. Declarative templates assign it -automatically during template resolution. Non-declarative users can provide a -manual schema on the definition, or pass one directly to `observerMap({ schema -})`. - -```ts -import { Schema } from "@microsoft/fast-element"; -import { observerMap } from "@microsoft/fast-element/observer-map.js"; - -const schema = new Schema("my-element"); -MyElement.define({ name: "my-element" }, [observerMap({ schema })]); -``` - -### `debug.js` requires explicit enablement - -`@microsoft/fast-element` no longer configures FAST just by being -imported. Call `enableDebug()` explicitly instead: - -```ts -// Before -import "@microsoft/fast-element"; - -// After -import { enableDebug } from "@microsoft/fast-element"; - -enableDebug(); -``` - -If you want debug behavior enabled automatically, keep using the root package -`development` export or the debug rollup bundle. - -### `FASTElement.compose()` removed - -`FASTElement.compose()` and subclass `compose()` calls have been removed from -the public authoring API. If the code immediately registers the element, call -`define()` on the subclass instead: - -```ts -// Before -MyElement.compose({ - name: "my-element", - template, - styles, -}).define(); - -// After -await MyElement.define({ - name: "my-element", - template, - styles, -}); -``` - -### `defineAsync()` and `composeAsync()` removed - -`FASTElement.defineAsync()` and `FASTElementDefinition.composeAsync()` have been -removed. Use subclass `define()` instead; it now returns a `Promise` that -resolves immediately for concrete templates and resolves after -`declarativeTemplate()` receives matching `` markup. - -See the [package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md) -for complete `define()`, `compose()`, and `register()` API mappings. - -### `RenderableFASTElement` removed (`@microsoft/fast-html`) - -The `RenderableFASTElement` mixin has been removed. Components extend `FASTElement` and call `define()` directly. - -2.x Example: -```ts -import { RenderableFASTElement } from "@microsoft/fast-html"; - -RenderableFASTElement(MyComponent).defineAsync({ - name: "my-component", - templateOptions: "defer-and-hydrate", -}); -``` - -3.x Example: -```ts -MyComponent.define({ - name: "my-component", -}); -``` - -### `TemplateOptions` removed - -`TemplateOptions`, `PartialFASTElementDefinition.templateOptions`, and -`FASTElementDefinition.templateOptions` have been removed. Remove -`templateOptions` from element definitions and use -`template: declarativeTemplate()` when declarative markup should supply the -template. - -See the [package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md) -for details on delayed template assignment behavior. - -### Bare `e` removed from declarative event handlers - -Declarative event handlers now reserve only `$e` for the DOM event and `$c` -for the execution context. Bare `e` is no longer treated as the event object. - -Before: -```html - -``` - -After: -```html - -``` - -If you used `TemplateParser.hasDeprecatedEventSyntax` in custom tooling, remove -that check as part of the migration. - -### `prepare()` lifecycle hook removed (`@microsoft/fast-html`) - -The `prepare()` hook is no longer available. Move initialization logic to `connectedCallback`: - -2.x Example: -```ts -class MyComponent extends FASTElement { - async prepare() { - this.data = await fetchData(); - } -} -``` - -3.x Example: -```ts -class MyComponent extends FASTElement { - connectedCallback() { - super.connectedCallback(); - this.loadData(); - } - async loadData() { - this.data = await fetchData(); - } -} -``` - -### `ViewTemplate.render()` signature unchanged - -The `isPrerendered` parameter that was briefly added to `ViewTemplate.render()` has been removed. The prerendered path is handled entirely by the pluggable hydration hook installed via `ElementController.installHydrationHook()`. - -### `globalThis.FAST` removed - -The `FAST` object is no longer attached to `globalThis`. It is now a module-scoped export from `@microsoft/fast-element`. - -2.x Example: -```ts -globalThis.FAST.addMessages({ ... }); -``` - -3.x Example: -```ts -import { FAST } from "@microsoft/fast-element"; - -FAST.addMessages({ ... }); -``` - -### `FAST.versions` and `fast-kernel` modes removed - -`FAST.versions` has been removed and multiple FAST versions on the same page are -unsupported in v3. Remove runtime checks that read `FAST.versions` and fix -duplicate FAST installs in your bundler or dependency graph instead. - -The `fast-kernel="share"`, `fast-kernel="share-v2"`, and -`fast-kernel="isolate"` script attributes no longer affect FAST initialization. -Remove them during migration. - -### `FAST.getById()` removed - -The `FAST.getById(id, initializer)` slot registry has been removed. Kernel services (update queue, observable system, etc.) are resolved through standard ES module imports. - -### `FASTGlobal` type removed - -The `FASTGlobal` interface type has been removed. Code that referenced this type should be updated to use the `FAST` export directly. - -### `KernelServiceId` enum removed - -The `KernelServiceId` enum (used with `FAST.getById()`) has been removed. Import kernel services directly from their respective modules. - -### `css` moved to `@microsoft/fast-element` - -`css`, `CSSTemplateTag`, and `CSSValue` are imported from `@microsoft/fast-element`. Other style APIs (`ElementStyles`, `CSSDirective`, `cssDirective`, `ComposableStyles`, `HostBehavior`, `HostController`, `StyleStrategy`, and `StyleTarget`) are imported from `@microsoft/fast-element`. - -Update imports to: -```ts -import { css, ElementStyles } from "@microsoft/fast-element"; -``` - -### Dynamic stylesheet behaviors removed - -`ElementStyles.withBehaviors()`, `ElementStyles.behaviors`, CSS bindings inside -`css`, and behavior-producing CSS directives have been removed. Keep -`ElementStyles` static, move runtime conditions into the element or controller -lifecycle, and call `this.$fastController.addStyles()` / -`this.$fastController.removeStyles()` as conditions change. - -See the [package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md) -for detailed dynamic stylesheet migration steps. - -### `ArrayObserver` moved to `@microsoft/fast-element` - -`ArrayObserver` is imported from `@microsoft/fast-element`. Other array helpers such as `Splice`, `SpliceStrategy`, `SpliceStrategySupport`, `lengthOf`, `sortedCount`, and `Sort` remain available from `@microsoft/fast-element`. - -Update imports to: -```ts -import { ArrayObserver } from "@microsoft/fast-element"; -``` - -### `deferHydrationAttribute` moved to `@microsoft/fast-element/hydration.js` - -`deferHydrationAttribute` is no longer exported by the root package. Most apps -can remove `defer-hydration` from server-rendered markup; if you still need the -legacy attribute string, import it from the hydration path export. - -In 2.x, `deferHydrationAttribute` was exported by the root package. - -2.x Example: -```ts -const attribute = "defer-hydration"; -``` - -3.x Example: -```ts -import { deferHydrationAttribute } from "@microsoft/fast-element/hydration.js"; -``` - -### `requestIdleCallback` / `cancelIdleCallback` polyfill removed - -FAST Element v3 no longer installs or depends on a `requestIdleCallback` / -`cancelIdleCallback` fallback. You do not need to polyfill those APIs for FAST -Element itself. The async `Updates` queue schedules DOM work with -`requestAnimationFrame`, so provide a `requestAnimationFrame` polyfill before -importing FAST Element when running in a non-browser environment that lacks it. - -### Hydration marker format changed - -The SSR hydration markers have been simplified from verbose, index-embedded comments to compact sequential markers. This is a breaking change to the SSR output format — SSR and client versions must match. - -| Old format | New format | -|---|---| -| `fe-b$$start$$$$$$fe-b` | `fe:b` | -| `fe-b$$end$$$$$$fe-b` | `fe:/b` | -| `fe-repeat$$start$$$$fe-repeat` | `fe:r` | -| `fe-repeat$$end$$$$fe-repeat` | `fe:/r` | -| `fe-eb$$start$$$$fe-eb` | `fe:e` | -| `fe-eb$$end$$$$fe-eb` | `fe:/e` | -| `data-fe-b="0 1 2"` / `data-fe-b-0` / `data-fe-c-0-3` | `data-fe="N"` | - -The `HydrationMarkup` API methods have been renamed (e.g., `parseAttributeBinding` → `parseAttributeBindingCount`) and no longer accept index/scope parameters. See the [package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md#hydration-marker-format-v3) for the complete API mapping. - -### `booleanConverter` aligned with native HTML boolean attribute semantics - -`@attr({ mode: "boolean" })` and `booleanConverter` now match how the platform treats native boolean attributes (e.g., `disabled`, `required`): - -- **Attribute → property** is presence-based: `setAttribute(name, anyString)` (including the empty string) sets the property to `true`; only `removeAttribute(name)` makes it `false`. -- **Property → attribute** uses `Boolean()` coercion: any JavaScript truthy value adds the attribute (with value `""`); any falsy value removes it. - -The headline difference is that `setAttribute(name, "false")` now resolves to property `true`, matching `` still being disabled. - -2.x Behavior: -```html - -``` -```ts -element.myBool === false; -``` - -3.x Behavior: -```html - -``` -```ts -element.myBool === true; -``` - -`booleanConverter` shape: - -```ts -toView(value: any) { - return value ? "" : null; -} - -fromView(value: any) { - return !!value; -} -``` - -`booleanConverter` 2.x → 3.x result differences: - -| Call | 2.x | 3.x | -|---|---|---| -| `fromView("false")` | `false` | `true` | -| `fromView("")` | `true` | `false` | -| `fromView(NaN)` | `true` | `false` | -| `toView(true)` (or any truthy) | `"true"` | `""` | -| `toView(false)` (or any falsy) | `"false"` | `null` | - -For `mode: "boolean"`, FAST writes attributes via `DOM.setBooleanAttribute` (presence-based) and reads them as `value !== null`, so the `mode: "boolean"` reflection path does **not** route through `booleanConverter.toView()` or `booleanConverter.fromView()`. The converter methods still apply to property assignment (`el.bool = X` runs `fromView(X)`), explicit calls (`booleanConverter.fromView(x)`), and `booleanConverter` paired with `mode: "reflect"` (which calls `toView`). - -**Migration:** - -- Audit `boolean`-mode attributes for the literal string `"false"` in templates or SSR HTML — the resulting property will be `true`, not `false`. To express the falsy state, omit the attribute entirely (or call `element.removeAttribute("my-bool")`). Assigning `element.myBool = false` continues to remove the reflected attribute as before. -- Audit code that calls `booleanConverter.toView()` directly or pairs `booleanConverter` with `mode: "reflect"`. The output strings are no longer `"true"` / `"false"`. -- If you need a tri-state attribute that preserves an explicit `"false"` string value, use `nullableBooleanConverter` with `mode: "reflect"` instead of `mode: "boolean"`. - -## New Exports - -| Export | Package | Description | -|---|---|---| -| `ElementController.isPrerendered` | `fast-element` | `Promise` — resolves `true` when element had DSD at connect time | -| `ElementController.isHydrated` | `fast-element` | `Promise` — resolves `true` only when hydration ran successfully | -| `enableHydration()` | `fast-element/hydration.js` | Enables hydration support for FAST elements | -| `deferHydrationAttribute` | `fast-element/hydration.js` | Legacy `defer-hydration` attribute string for compatibility code | -| `HydrationTracker` | `fast-element/hydration.js` | Standalone hydration lifecycle tracker class | -| `HydrationOptions` | `fast-element/hydration.js` | Type for hydration configuration options | -| `ViewController.isPrerendered` | `fast-element/templating.js` | `Promise` — DSD detection for custom directives | -| `ViewController.isHydrated` | `fast-element/templating.js` | `Promise` — hydration status for custom directives | diff --git a/sites/website/src/docs/3.x/migration-guide/core.md b/sites/website/src/docs/3.x/migration-guide/core.md new file mode 100644 index 00000000000..0f7a25f473e --- /dev/null +++ b/sites/website/src/docs/3.x/migration-guide/core.md @@ -0,0 +1,358 @@ +--- +id: migration-guide-core +title: Core Migration from 2.x to 3.x +layout: 3x +eleventyNavigation: + key: migration-guide-core3x + parent: migration-guide3x + title: Core + order: 1 +navigationOptions: + activeKey: migration-guide-core3x +keywords: + - migrate + - migration +--- + +# Core migration from 2.x to 3.x + +FAST Element 3.x focuses the client runtime on browser `Window` environments and +removes several APIs that previously supported alternate registration, +hydration, and declarative HTML flows. Complete this core migration for every +application, then follow the add-on migration path that matches the extra +functionality your application uses. + +| Migration path | Use it when | +|---|---| +| Core FAST Element migration | You author components with `FASTElement`, `html`, `css`, `define()`, `compose()`, `@attr`, `FAST`, or `ElementStyles.withBehaviors()`. | +| [Hydration and SSR migration](/docs/3.x/migration-guide/hydration/) | You server-rendered FAST components, emitted FAST hydration markers, used `@microsoft/fast-ssr`, or installed FAST hydration helpers. | +| [Declarative HTML migration](/docs/3.x/migration-guide/declarative-html/) | You used the removed `@microsoft/fast-html` package, ``, `RenderableFASTElement`, `TemplateElement`, or declarative map options. | + +## Audit your application + +Before changing code, review your application for removed APIs, changed imports, +and add-on migration triggers. + +| Area | Look for | What to update | +|---|---|---| +| Element registration | `compose()`, `defineAsync()`, `composeAsync()`, `registerAsync()` | Replace owned element registration with subclass `define()`. Use `fastElementRegistry.whenRegistered()` only when code needs to wait for a tag name owned elsewhere. | +| FAST global and kernel usage | `globalThis.FAST`, `FAST.versions`, `FAST.getById()`, `KernelServiceId`, or `fast-kernel` script attributes | Import FAST services directly from `@microsoft/fast-element` and remove runtime version/kernel configuration. | +| Styles | `ElementStyles.withBehaviors()`, `ElementStyles.behaviors`, CSS bindings inside `css`, or behavior-producing CSS directives | Move runtime style decisions into element/controller lifecycle code and add or remove styles as state changes. | +| Boolean attributes | FAST boolean-mode attributes that render literal string values such as `"false"`, or direct calls to `booleanConverter` | Omit boolean attributes to represent `false`. Review direct converter usage because 3.x follows native HTML boolean attribute semantics. | +| Hydration and SSR | `@microsoft/fast-ssr`, `HydratableElementController`, hydration marker inspection, `defer-hydration`, `needs-hydration`, or hydration side-effect imports | Complete the [Hydration and SSR migration](/docs/3.x/migration-guide/hydration/) after this core migration. | +| Declarative HTML | `@microsoft/fast-html`, ``, `RenderableFASTElement`, `TemplateElement`, `TemplateOptions`, `templateOptions`, or declarative `attributeMap` / `observerMap` options | Complete the [Declarative HTML migration](/docs/3.x/migration-guide/declarative-html/) after this core migration. | + +## Recommended upgrade order + +1. Replace `compose()`, `defineAsync()`, and `composeAsync()` registration flows + with subclass `define()`. +2. Update imports that moved to changed public package paths. +3. Move dynamic stylesheet behavior out of `css` and into the element or + controller lifecycle. +4. Update your package versions so all FAST packages are on matching 3.x + versions. Do not mix old SSR output or declarative runtime code with the 3.x + client. +5. If you server-render or hydrate FAST components, complete the + [Hydration and SSR migration](/docs/3.x/migration-guide/hydration/). +6. If you use declarative HTML, complete the + [Declarative HTML migration](/docs/3.x/migration-guide/declarative-html/). +7. Rebuild and manually verify the upgraded components in the browser. + +## Runtime requirements + +FAST Element 3.x targets client-side browser `Window` runtimes. Do not import or +run the FAST Element client runtime in workers, server-side JavaScript runtimes, +or other non-window hosts. Server rendering tools may emit HTML, but hydration +and client rendering run in the browser window where DOM, Custom Elements, +Shadow DOM, and `requestAnimationFrame` are available. + +FAST Element 3.x also no longer installs a `globalThis` polyfill and no longer +installs or depends on a `requestIdleCallback` / `cancelIdleCallback` fallback. +Load a `globalThis` polyfill before importing FAST only if you still support an +older browser that lacks it. You do not need to polyfill `requestIdleCallback` +or `cancelIdleCallback` for FAST Element itself. + +See the +[package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md#native-globals-and-scheduler-requirements-v2--v3) +for the complete native globals and scheduler migration steps. + +## Package and path exports + +The root `@microsoft/fast-element` import remains supported. Update only the +public imports that moved from older nested paths to focused package paths. + +| Look for | Use | Named exports | +|---|---|---| +| `@microsoft/fast-element/binding/two-way.js` | `@microsoft/fast-element/two-way.js` | `twoWay`, `TwoWayBindingOptions`, `TwoWaySettings` | +| `@microsoft/fast-element/binding/signal.js` | `@microsoft/fast-element/signal.js` | `signal`, `Signal` | +| Older nested `children` directive imports | `@microsoft/fast-element/children.js` | `children`, `ChildrenDirective`, `ChildListDirectiveOptions`, `ChildrenDirectiveOptions`, `SubtreeDirectiveOptions` | +| Older nested `ref` directive imports | `@microsoft/fast-element/ref.js` | `ref`, `RefDirective` | +| Older nested `repeat` directive imports | `@microsoft/fast-element/repeat.js` | `repeat`, `RepeatBehavior`, `RepeatDirective`, `RepeatOptions` | +| Older nested `slotted` directive imports | `@microsoft/fast-element/slotted.js` | `slotted`, `SlottedDirective`, `SlottedDirectiveOptions` | +| Older nested `when` directive imports | `@microsoft/fast-element/when.js` | `when` | + +Hydration and declarative HTML import changes are covered in their add-on +migration pages. See the [Path Exports](/docs/3.x/advanced/path-exports/) +reference for the full current package export list. + +## Element registration + +### `FASTElement.compose()` removed + +`FASTElement.compose()` and subclass `compose()` calls have been removed from the +public authoring API. If your code immediately registered the element after +composing it, call `define()` on the subclass instead. + +```ts +// Before +MyElement.compose({ + name: "my-element", + template, + styles, +}).define(); + +// After +MyElement.define({ + name: "my-element", + template, + styles, +}); +``` + +### `defineAsync()` and `composeAsync()` removed + +`FASTElement.defineAsync()` and `FASTElementDefinition.composeAsync()` have been +removed. Use subclass `define()` instead. The returned `Promise` is available +only for code that explicitly needs to observe registration completion. + +```ts +// Before +MyElement.defineAsync({ + name: "my-element", + template, + styles, +}); + +// After +MyElement.define({ + name: "my-element", + template, + styles, +}); +``` + +Replace `FASTElementDefinition.compose()` calls that chain `.define()` the same +way: + +```ts +// Before +FASTElementDefinition.compose(MyElement, options).define(); + +// After +MyElement.define(options); +``` + +If old code used `registerAsync()` only to wait for a custom element tag name to +become available, replace that wait with `fastElementRegistry.whenRegistered()`. +Use subclass `define()` when the code owns the element definition. + +See the +[package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md#async-definecomposeregister-api-v3) +for complete `define()`, `compose()`, and `register()` API mappings. + +## Registry lookups + +The static `FASTElementDefinition.isRegistered` map is no longer public. Import +`fastElementRegistry` from `@microsoft/fast-element/registry.js` instead. + +```ts +import { fastElementRegistry } from "@microsoft/fast-element/registry.js"; + +const definition = await fastElementRegistry.whenRegistered("my-element"); +``` + +Use `fastElementRegistry.getByType(type)` or +`fastElementRegistry.getForInstance(instance)` when you already have a +constructor or element instance. + +## FAST global changes + +### `globalThis.FAST` removed + +The `FAST` object is no longer attached to `globalThis`. It is now a +module-scoped export from `@microsoft/fast-element`. + +```ts +// Before +globalThis.FAST.addMessages({ ... }); + +// After +import { FAST } from "@microsoft/fast-element"; + +FAST.addMessages({ ... }); +``` + +### `FAST.versions` and `fast-kernel` modes removed + +`FAST.versions` has been removed and multiple FAST versions on the same page are +unsupported in 3.x. Remove runtime checks that read `FAST.versions` and fix +duplicate FAST installs in your bundler or dependency graph instead. + +The `fast-kernel="share"`, `fast-kernel="share-v2"`, and +`fast-kernel="isolate"` script attributes no longer affect FAST initialization. +Remove them during migration. + +### `FAST.getById()` and kernel service IDs removed + +The `FAST.getById(id, initializer)` slot registry has been removed. Kernel +services such as the update queue and observable system are resolved through +standard ES module imports. Remove `FASTGlobal` and `KernelServiceId` type usage +and import the service APIs directly. + +## Debug entrypoint + +`@microsoft/fast-element/debug.js` no longer configures FAST just by being +imported. Call `enableDebug()` explicitly instead. + +```ts +// Before +import "@microsoft/fast-element/debug.js"; + +// After +import { enableDebug } from "@microsoft/fast-element/debug.js"; + +enableDebug(); +``` + +If you want debug behavior enabled automatically, keep using the root package +`development` export or the debug rollup bundle. + +## CSS and styles + +### `css` moved to `@microsoft/fast-element` + +`css`, `CSSTemplateTag`, and `CSSValue` are imported from +`@microsoft/fast-element`. Other style APIs such as `ElementStyles`, +`CSSDirective`, `cssDirective`, `ComposableStyles`, `HostBehavior`, +`HostController`, `StyleStrategy`, and `StyleTarget` are also available from the +root package export. + +```ts +import { css, ElementStyles } from "@microsoft/fast-element"; +``` + +### Dynamic stylesheet behaviors removed + +`ElementStyles.withBehaviors()`, `ElementStyles.behaviors`, CSS bindings inside +`css`, and behavior-producing CSS directives have been removed. `ElementStyles` +is now a static style container. Move runtime conditions into the element or +controller lifecycle, then add and remove styles as conditions change. + +```ts +import { css, FASTElement } from "@microsoft/fast-element"; + +const compactStyles = css` + :host { + gap: 4px; + } +`; + +class MyElement extends FASTElement { + #media = globalThis.matchMedia("(max-width: 600px)"); + + connectedCallback() { + super.connectedCallback(); + this.#media.addEventListener("change", this.#syncCompactStyles); + this.#syncCompactStyles(); + } + + disconnectedCallback() { + this.#media.removeEventListener("change", this.#syncCompactStyles); + this.$fastController.removeStyles(compactStyles); + super.disconnectedCallback(); + } + + #syncCompactStyles = () => { + if (this.#media.matches) { + this.$fastController.addStyles(compactStyles); + } else { + this.$fastController.removeStyles(compactStyles); + } + }; +} +``` + +If you previously interpolated bindings or behavior-producing directives into +`css`, replace them with element state and standard DOM, class, attribute, +inline-style, or controller updates. + +See the +[package migration guide](https://github.com/microsoft/fast/blob/main/packages/fast-element/docs/migration/fast-element-3.md#dynamic-stylesheet-behaviors-v3) +for detailed dynamic stylesheet migration steps. + +## Attribute and converter behavior + +### `booleanConverter` now matches native HTML boolean attributes + +`@attr({ mode: "boolean" })` and `booleanConverter` now match how the platform +treats native boolean attributes such as `disabled` and `required`: + +- Attribute to property is presence-based: `setAttribute(name, anyString)` + sets the property to `true`; only `removeAttribute(name)` makes it `false`. +- Property to attribute uses `Boolean()` coercion: any JavaScript truthy value + adds the attribute with value `""`; any falsy value removes it. + +The main 2.x to 3.x difference is that `setAttribute(name, "false")` now resolves +to property `true`. + +```html + +``` + +```ts +// 2.x +element.myBool === false; + +// 3.x +element.myBool === true; +``` + +`booleanConverter` 2.x to 3.x result differences: + +| Call | 2.x | 3.x | +|---|---|---| +| `fromView("false")` | `false` | `true` | +| `fromView("")` | `true` | `false` | +| `fromView(NaN)` | `true` | `false` | +| `toView(true)` or any truthy value | `"true"` | `""` | +| `toView(false)` or any falsy value | `"false"` | `null` | + +For `mode: "boolean"`, FAST writes attributes through presence-based boolean +attribute logic and reads them as `value !== null`. The converter methods still +apply to property assignment, explicit converter calls, and `booleanConverter` +paired with `mode: "reflect"`. + +Migration steps: + +1. Audit boolean-mode attributes for the literal string `"false"` in templates + or SSR HTML. Omit the attribute to express the falsy state. +2. Audit code that calls `booleanConverter.toView()` directly or pairs + `booleanConverter` with `mode: "reflect"`. +3. If you need a tri-state attribute that preserves an explicit `"false"` string + value, use `nullableBooleanConverter` with `mode: "reflect"` instead of + `mode: "boolean"`. + +## Core exports added in 3.x + +| Export | Package | Description | +|---|---|---| +| `ElementController.isPrerendered` | `@microsoft/fast-element` | `Promise` that resolves `true` when an element had Declarative Shadow DOM at connect time. | +| `ElementController.isHydrated` | `@microsoft/fast-element` | `Promise` that resolves `true` only when hydration ran successfully. | +| `ViewController.isPrerendered` | `@microsoft/fast-element` | `Promise` for custom directives that need prerender detection. | +| `ViewController.isHydrated` | `@microsoft/fast-element` | `Promise` for custom directives that need hydration status. | +| `fastElementRegistry` | `@microsoft/fast-element/registry.js` | Public registry lookup helper for registered FAST element definitions. | + +Hydration-specific exports are covered in the +[Hydration and SSR migration](/docs/3.x/migration-guide/hydration/), and +declarative HTML exports are covered in the +[Declarative HTML migration](/docs/3.x/migration-guide/declarative-html/). diff --git a/sites/website/src/docs/3.x/migration-guide/declarative-html.md b/sites/website/src/docs/3.x/migration-guide/declarative-html.md new file mode 100644 index 00000000000..f37feb52366 --- /dev/null +++ b/sites/website/src/docs/3.x/migration-guide/declarative-html.md @@ -0,0 +1,323 @@ +--- +id: migration-guide-declarative-html +title: Declarative HTML Migration +layout: 3x +eleventyNavigation: + key: migration-guide-declarative-html3x + parent: migration-guide3x + title: Declarative HTML + order: 3 +navigationOptions: + activeKey: migration-guide-declarative-html3x +keywords: + - migrate + - migration + - declarative + - fast-html + - f-template +--- + +{% raw %} + +# Declarative HTML migration + +Use this add-on path after the core migration if your v2 application used +`@microsoft/fast-html`, ``, `RenderableFASTElement`, +`TemplateElement`, declarative `attributeMap` or `observerMap` options, or +declarative event handlers. Complete the +[core migration](/docs/3.x/migration-guide/core/) first. If the declarative templates +were also server-rendered or hydrated, complete the +[Hydration and SSR migration](/docs/3.x/migration-guide/hydration/) as well. + +## What changed + +The `@microsoft/fast-html` package has been removed. Declarative HTML now ships +from focused `@microsoft/fast-element` path exports and integrates with normal +`FASTElement.define()` calls. + +Key migration points: + +1. Import declarative APIs from `@microsoft/fast-element` path exports. +2. Replace `RenderableFASTElement(...).defineAsync()` with subclass `define()`. +3. Replace public `TemplateElement` setup with `template: declarativeTemplate()`. +4. Replace `TemplateElement.options()` with define extensions. +5. Replace `prepare()` with standard element lifecycle code. +6. Update declarative event handlers to use `$e` instead of bare `e`. + +## Update imports + +Import declarative HTML APIs from FAST Element path exports instead of +`@microsoft/fast-html`. + +```ts +// Before +import { TemplateElement } from "@microsoft/fast-html"; +import { deepMerge } from "@microsoft/fast-html/utilities.js"; + +// After +import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; +import { deepMerge } from "@microsoft/fast-element/declarative-utilities.js"; +``` + +Map helpers are define extensions with their own path exports. + +```ts +import { attributeMap } from "@microsoft/fast-element/attribute-map.js"; +import { observerMap } from "@microsoft/fast-element/observer-map.js"; +``` + +Keep importing core FAST Element APIs from the root package export. + +| API | Import path | +|---|---| +| `FASTElement`, `FAST`, `ElementController`, definition/controller types | `@microsoft/fast-element` | +| `attr`, `AttributeDefinition`, converters, `ValueConverter` | `@microsoft/fast-element` | +| `Observable`, `observable`, `volatile`, `Updates` | `@microsoft/fast-element` | +| `html`, `css`, `ViewTemplate`, `HTMLView` | `@microsoft/fast-element` | +| `Schema`, `schemaRegistry`, schema types | `@microsoft/fast-element` | +| `Binding`, `oneWay`, `oneTime`, `listener` | `@microsoft/fast-element` | +| `DOM`, `DOMAspect`, `DOMPolicy` | `@microsoft/fast-element` | +| `children`, `elements`, `ref`, `repeat`, `slotted`, `when` | `@microsoft/fast-element` | + +## Replace `RenderableFASTElement` + +The `RenderableFASTElement` mixin has been removed. Components extend +`FASTElement` and call `define()` directly. + +```ts +// Before +import { RenderableFASTElement } from "@microsoft/fast-html"; + +RenderableFASTElement(MyComponent).defineAsync({ + name: "my-component", + templateOptions: "defer-and-hydrate", +}); +``` + +```ts +// After +import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; + +MyComponent.define({ + name: "my-component", + template: declarativeTemplate(), +}); +``` + +`declarativeTemplate()` coordinates template resolution. If code explicitly +observes the Promise returned by `define()`, it resolves after the matching +`` supplies the concrete template. If prerendered content exists in +the DOM, call `enableHydration()` before elements connect to hydrate it. + +## Replace public `TemplateElement` setup + +The `` implementation is now internal and is defined automatically +when an element uses `template: declarativeTemplate()`. + +| Removed | Replacement | +|---|---| +| `TemplateElement` public export | `declarativeTemplate()` | +| `TemplateElement.define({ name: "f-template" })` | No manual definition needed. | +| `TemplateElement.config(callbacks)` | `enableHydration().whenHydrated(tagName)` for tag-specific hydration waits and `enableHydration().whenHydrated()` for the active hydration batch. | +| `TemplateElement.options(...)` | `attributeMap()` and `observerMap()` define extensions. | +| `AttributeMap` / `ObserverMap` class exports from the old declarative public surface | `attributeMap()` / `observerMap()` helpers and config types. | + +```ts +// Before +import { TemplateElement } from "@microsoft/fast-html"; + +TemplateElement.define({ name: "f-template" }); +TemplateElement.options({ + "my-element": { + attributeMap: true, + observerMap: true, + }, +}); + +MyElement.define({ name: "my-element" }); +``` + +```ts +// After +import { attributeMap } from "@microsoft/fast-element/attribute-map.js"; +import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; +import { observerMap } from "@microsoft/fast-element/observer-map.js"; + +MyElement.define( + { + name: "my-element", + template: declarativeTemplate(), + }, + [attributeMap(), observerMap()], +); +``` + +`FASTElementDefinition.schema` is optional. Declarative templates assign it +automatically during template resolution. Non-declarative users can provide a +manual schema on the definition, or pass one directly to `observerMap({ +schema })`. + +```ts +import { Schema } from "@microsoft/fast-element"; +import { observerMap } from "@microsoft/fast-element/observer-map.js"; + +const schema = new Schema("my-element"); + +MyElement.define({ name: "my-element" }, [observerMap({ schema })]); +``` + +## Remove `TemplateOptions` + +`TemplateOptions`, `PartialFASTElementDefinition.templateOptions`, and +`FASTElementDefinition.templateOptions` have been removed. Remove +`templateOptions` from element definitions and use +`template: declarativeTemplate()` when declarative markup should supply the +template. + +```ts +// Before +MyElement.define({ + name: "my-element", + templateOptions: "defer-and-hydrate", +}); + +// After +MyElement.define({ + name: "my-element", + template: declarativeTemplate(), +}); +``` + +Elements can still be defined before a template is attached. A later +`FASTElementDefinition.template` update notifies connected elements so they can +render or hydrate with the new template. + +## Update declarative attribute mapping + +Declarative attribute mapping now defaults to `"camelCase"` instead of `"none"`. +With the default strategy, `{{firstName}}` maps to the `firstName` property and +the `first-name` HTML attribute. If your v2 templates relied on literal +attribute names, configure both the client and server to use `"none"`. + +```ts +import { attributeMap } from "@microsoft/fast-element/attribute-map.js"; +import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; + +MyElement.define( + { + name: "my-element", + template: declarativeTemplate(), + }, + [attributeMap({ "attribute-name-strategy": "none" })], +); +``` + +If server-rendered HTML is generated with `@microsoft/fast-build`, pass the same +strategy to the CLI: + +```bash +fast build --attribute-name-strategy=none +``` + +## Update declarative event handlers + +Declarative event handlers now reserve only `$e` for the DOM event and `$c` for +the execution context. Bare `e` is no longer treated as the event object. + +```html + + + + + +``` + +If you used `TemplateParser.hasDeprecatedEventSyntax` in custom tooling, remove +that check as part of the migration. + +## Replace `prepare()` lifecycle logic + +The `prepare()` hook from `@microsoft/fast-html` is no longer available. Move +initialization logic to `connectedCallback()` or an application-owned async +method that is called from `connectedCallback()`. + +```ts +// Before +class MyComponent extends FASTElement { + async prepare() { + this.data = await fetchData(); + } +} +``` + +```ts +// After +class MyComponent extends FASTElement { + connectedCallback() { + super.connectedCallback(); + this.loadData(); + } + + async loadData() { + this.data = await fetchData(); + } +} +``` + +If `prepare()` previously guarded duplicate client fetches after SSR, use +`this.$fastController.isPrerendered` and `this.$fastController.isHydrated` to +decide whether client initialization should run. + +## Verify `` loading order + +A declarative FASTElement component requires a JavaScript class definition with +`template: declarativeTemplate()` and an `` whose `name` matches the +custom element tag name. + +```html + + + +``` + +```ts +import { attr, FASTElement } from "@microsoft/fast-element"; +import { declarativeTemplate } from "@microsoft/fast-element/declarative.js"; + +class MyCounter extends FASTElement { + @attr count: number = 0; +} + +MyCounter.define({ + name: "my-counter", + template: declarativeTemplate(), +}); +``` + +Template-first and definition-first loading both work, but the custom element +registration completes only after the matching template is available. A common +pattern is to include `` elements directly in the HTML page before +the script module loads. + +## Migration checklist + +1. Replace `@microsoft/fast-html` imports with + `@microsoft/fast-element/declarative.js` and + `@microsoft/fast-element/declarative-utilities.js`. +2. Replace `RenderableFASTElement(...).defineAsync()` with subclass `define()`. +3. Remove manual `TemplateElement.define()` calls. +4. Replace `TemplateElement.options()` with define extensions passed as the + second argument to `define()`. +5. Remove `templateOptions`; use `template: declarativeTemplate()` when + declarative markup supplies the template. +6. Configure `attributeMap({ "attribute-name-strategy": "none" })` only if you + need v2 literal attribute-name behavior, and align the server renderer option. +7. Replace bare `e` in event handlers with `$e`. +8. Move `prepare()` logic to standard element lifecycle code. +9. If declarative output is server-rendered, complete the + [Hydration and SSR migration](/docs/3.x/migration-guide/hydration/). + +{% endraw %} diff --git a/sites/website/src/docs/3.x/migration-guide/hydration.md b/sites/website/src/docs/3.x/migration-guide/hydration.md new file mode 100644 index 00000000000..f6a1fc4bdc7 --- /dev/null +++ b/sites/website/src/docs/3.x/migration-guide/hydration.md @@ -0,0 +1,268 @@ +--- +id: migration-guide-hydration +title: Hydration and SSR Migration +layout: 3x +eleventyNavigation: + key: migration-guide-hydration3x + parent: migration-guide3x + title: Hydration and SSR + order: 2 +navigationOptions: + activeKey: migration-guide-hydration3x +keywords: + - migrate + - migration + - hydration + - SSR + - fast-ssr +--- + +# Hydration and SSR migration + +Use this add-on path after the core migration if your v2 application +server-rendered FAST components, used `@microsoft/fast-ssr`, emitted FAST +hydration markers, imported hydration side-effect helpers, or inspected +hydration controller APIs. Complete the +[core migration](/docs/3.x/migration-guide/core/) first, then update the SSR and +hydration pipeline so the server output and browser runtime agree. + +## What changed + +FAST Element 3.x keeps the client runtime browser-only and makes hydration +explicit. The server can still emit HTML and Declarative Shadow DOM, but the +browser `Window` defines custom elements, runs `enableHydration()`, and attaches +bindings to the existing DOM. + +Key migration points: + +1. Hydration is opt-in with `enableHydration()`. +2. `HydratableElementController` and hydration side-effect imports are removed. +3. `needs-hydration` and `defer-hydration` are no longer required in rendered + markup. +4. Hydration callbacks are replaced by hydration promises. +5. SSR hydration marker syntax changed; server and client FAST versions must + match. + +## Install hydration explicitly + +The `HydratableElementController` class has been removed. Its functionality is +now built into `ElementController` through automatic prerendered content +detection. When a component connects with an existing shadow root from SSR or +Declarative Shadow DOM, FAST can hydrate the existing DOM if hydration was +enabled before the element connects. + +```ts +// Before +import { HydratableElementController } from "@microsoft/fast-element"; + +HydratableElementController.install(); +``` + +```ts +// After +import { enableHydration } from "@microsoft/fast-element/hydration.js"; + +enableHydration(); +``` + +Remove these side-effect imports: + +| Removed import | Replacement | +|---|---| +| `@microsoft/fast-element/install-element-hydration.js` | No side-effect import. Call `enableHydration()` before FAST elements connect. | +| `@microsoft/fast-element/install-hydration.js` | No side-effect import. Call `enableHydration()` before FAST elements connect. | +| `@microsoft/fast-element/install-hydratable-view-templates.js` | No side-effect import. `enableHydration()` installs hydratable template support. | + +Call `enableHydration()` before defining or connecting FAST elements. + +```ts +import { enableHydration } from "@microsoft/fast-element/hydration.js"; + +const hydration = enableHydration(); +await import("./components.js"); +await hydration.whenHydrated(); +``` + +If a prerendered element connects before `enableHydration()` runs, the existing +shadow root is treated as client-render fallback: `isPrerendered` resolves +`true`, `isHydrated` resolves `false`, and FAST replaces the prerendered content +with a client-rendered view. Calling `enableHydration()` later does not hydrate +elements that already completed their first render. + +## Remove legacy hydration attributes + +`needs-hydration` and `defer-hydration` are no longer needed in server-rendered +markup. Existing shadow root detection determines whether the element was +prerendered. + +```html + + + + +``` + +```html + + + + +``` + +The root `deferHydrationAttribute` export has also moved. Most applications can +remove `defer-hydration` entirely. If compatibility code still needs the legacy +attribute string, import it from the hydration path export. + +```ts +import { deferHydrationAttribute } from "@microsoft/fast-element/hydration.js"; +``` + +## Replace hydration callbacks with promises + +`HydrationControllerCallbacks` has been replaced by the controller returned from +`enableHydration()`. + +```ts +// Before +import { HydratableElementController } from "@microsoft/fast-element"; + +HydratableElementController.config({ + /* hydration callbacks */ +}); +``` + +```ts +// After +import { enableHydration } from "@microsoft/fast-element/hydration.js"; + +const hydration = enableHydration(); +await hydration.whenHydrated(); +``` + +Use `hydration.whenHydrated("my-element")` when application code needs to wait +for hydration work associated with a specific tag name. Use +`hydration.whenHydrated()` for the active global hydration batch. When +`stopHydration: StopHydration.never` is configured, the global promise +intentionally remains pending because hydration never reaches a global +completion point. + +## Use the split prerender and hydration state + +The `isPrerendered` property on `ElementController` and `ViewController` now +resolves `true` when the element had Declarative Shadow DOM at connect time, +regardless of whether hydration ran. The `isHydrated` property resolves `true` +only when hydration actually ran successfully. + +```ts +async connectedCallback() { + super.connectedCallback(); + + const prerendered = await this.$fastController.isPrerendered; + const hydrated = await this.$fastController.isHydrated; + + if (prerendered && !hydrated) { + // The element had SSR content but hydration was not enabled in time. + } +} +``` + +Replace `element.$fastController instanceof HydratableElementController` checks +with the `isPrerendered` and `isHydrated` promises. + +## Update SSR output markers + +The SSR hydration markers have been simplified from verbose, index-embedded +comments to compact sequential markers. This is a breaking change to the SSR +output format. SSR and client versions must match. + +| Old format | New format | +|---|---| +| `fe-b$$start$$$$$$fe-b` | `fe:b` | +| `fe-b$$end$$$$$$fe-b` | `fe:/b` | +| `fe-repeat$$start$$$$fe-repeat` | `fe:r` | +| `fe-repeat$$end$$$$fe-repeat` | `fe:/r` | +| `fe-eb$$start$$$$fe-eb` | `fe:e` | +| `fe-eb$$end$$$$fe-eb` | `fe:/e` | +| `data-fe-b="0 1 2"` / `data-fe-b-0` / `data-fe-c-0-3` | `data-fe="N"` | + +The `HydrationMarkup` API methods have also changed. + +| Removed or changed API | Replacement | +|---|---| +| `HydrationMarkup.contentBindingStartMarker(index, scopeId)` | `HydrationMarkup.contentBindingStartMarker()` | +| `HydrationMarkup.contentBindingEndMarker(index, scopeId)` | `HydrationMarkup.contentBindingEndMarker()` | +| `HydrationMarkup.parseAttributeBinding(element)` | `HydrationMarkup.parseAttributeBindingCount(element)` | +| `HydrationMarkup.parseRepeatStartMarker(data)` | `HydrationMarkup.isRepeatViewStartMarker(data)` | +| `HydrationMarkup.parseRepeatEndMarker(data)` | `HydrationMarkup.isRepeatViewEndMarker(data)` | +| `HydrationMarkup.parseElementBoundaryStartMarker(content)` | `HydrationMarkup.isElementBoundaryStartMarker(node)` | +| `HydrationMarkup.parseElementBoundaryEndMarker(content)` | `HydrationMarkup.isElementBoundaryEndMarker(node)` | + +If you used `@microsoft/fast-ssr` or custom SSR tooling, update the renderer to emit the +new marker format before loading the v3 client. If you use `@microsoft/fast-build`, +upgrade it with `@microsoft/fast-element` and rebuild the output. + +## Keep renderer and client versions in sync + +Hydration succeeds when the HTML produced by the server matches the template and +data that the client runtime sees during the element's first render. The +renderer and client both rely on the same depth-first binding order and marker +syntax. Deploy server-rendered output and client bundles together so old marker +output is not hydrated by the v3 client, and the v3 marker output is not loaded +with an older client. + +Do not minify or sanitize away FAST comments or `data-fe` attributes before the +client loads. A missing `fe:/b` marker, an invalid `data-fe` count, or a changed +DOM shape means the hydration walker cannot target the compiled bindings. + +## Troubleshooting hydration + +If hydration does not run, FAST renders client-side. If hydration starts and +detects a recoverable mismatch, such as empty `render()` view boundaries or a +`repeat()` whose SSR item count disagrees with the client array, it reconciles by +falling back to the client view or by creating/removing the affected items. If +the mismatch cannot be reconciled, FAST throws either `HydrationBindingError` or +`HydrationTargetElementError`. + +Pass `hydrationDebugger()` through `enableHydration()` to replace the default +one-line mismatch message with an "Expected / Received" report and structured +`expected` and `received` fields. + +```ts +import { + enableHydration, + hydrationDebugger, +} from "@microsoft/fast-element/hydration.js"; + +enableHydration({ debugger: hydrationDebugger() }); +``` + +With the debugger installed, an unrecoverable mismatch produces output such as: + +```text +Hydration mismatch in . + Expected: with content binding + Received: server +``` + +## Hydration exports added in 3.x + +| Export | Package | Description | +|---|---|---| +| `enableHydration()` | `@microsoft/fast-element/hydration.js` | Enables hydration support for FAST elements. | +| `deferHydrationAttribute` | `@microsoft/fast-element/hydration.js` | Legacy `defer-hydration` attribute string for compatibility code. | +| `HydrationTracker` | `@microsoft/fast-element/hydration.js` | Standalone hydration lifecycle tracker class. | +| `HydrationOptions` | `@microsoft/fast-element/hydration.js` | Type for hydration configuration options. | + +## Migration checklist + +1. Remove `HydratableElementController.install()` and + `HydratableElementController.config()` usage. +2. Remove hydration side-effect imports. +3. Call `enableHydration()` before FAST elements connect. +4. Replace hydration callbacks with `whenHydrated()` waits. +5. Replace prerender checks with `isPrerendered` and `isHydrated`. +6. Remove `needs-hydration` and `defer-hydration` from SSR markup unless + compatibility code still explicitly needs the legacy attribute string. +7. Update `@microsoft/fast-ssr`, `@microsoft/fast-build`, or custom SSR output to the v3 + hydration marker format. +8. Deploy matching renderer and client versions.