feat: modularize FASTElement with opt-in hydration and subpath exports#7497
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactor hydration to be opt-in via enableHydration() from @microsoft/fast-element/hydration.js. Per-element lifecycle callbacks (elementWillHydrate, elementDidHydrate, etc.) are now passed directly to declarativeTemplate(). Key changes: - Add enableHydration() function and ./hydration.js subpath export - declarativeTemplate() now accepts optional lifecycle callbacks - Hydration is no longer active unless enableHydration() is called - isPrerendered resolves true only when hydration actually occurs - renderPrerendered wrapped in try/finally for error safety - HydrationTracker simplified to global callbacks only - TemplateElement.config() kept for backward compatibility - TemplateLifecycleCallbacks extended with all 6 callbacks - Add declarative-no-hydration test fixture - Add lifecycle-callbacks.md to 3.x website docs - Update DESIGN.md, README.md, migration guide Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore fixture HTML files that were inadvertently reformatted by biome. Rebuild all packages from clean state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split isPrerendered into two separate promises: - isPrerendered: resolves true when DSD shadow root existed at connect - isHydrated: resolves true only when hydration actually ran Fix templates.html formatting that broke declarative bindings. Update DESIGN.md, README.md, DECLARATIVE_RENDERING_LIFECYCLE.md, migration guide, and lifecycle-callbacks docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DESIGN.md: FAST Global → FAST Object (module-scoped, no globalThis), ElementController uses composition instead of inheritance, pluggable hydration hook via installHydrationHook(), styles subpath export, remove KernelServiceId references - README.md: remove requestIdleCallback polyfill mention, add styles.js subpath import section - migration-guide.md: add breaking changes for globalThis.FAST removal, FAST.getById/FASTGlobal/KernelServiceId removal, css/styles moved to styles.js, array observation moved to arrays.js, deferHydrationAttribute moved to hydration.js, requestIdleCallback polyfill removal - fast-element.md: update css import to use styles.js subpath Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Breaking changes to reduce FASTElement bundle size: - Remove globalThis.FAST singleton — FAST is now a module-scoped export, no side-effecting global mutation - Remove FAST.getById(), FASTGlobal type, KernelServiceId enum - Remove requestIdleCallback polyfill (native in all modern browsers) - Move HydrationView to separate module (hydration-view.ts) - Move renderPrerendered to pluggable hydration hook in enable-hydration.ts — ElementController has zero hydration imports - Move CSS/styles to ./styles.js subpath export - Move array observation to ./arrays.js subpath export - Move deferHydrationAttribute to ./hydration.js subpath (retained for intersection observer viewport hydration rendering) - ElementController uses composition instead of inheritance for PropertyChangeNotifier - Update DESIGN.md, README.md, migration guide, 3.x docs - Fix fast-router and todo-app imports for new subpaths - Update platform.pw.spec.ts and debug.pw.spec.ts for module FAST Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update measure-sizes.js to measure exports from subpath entry points (styles.js, hydration.js, arrays.js) instead of only the main barrel. Fixes css showing 0 B after moving to subpath. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu
left a comment
There was a problem hiding this comment.
Deep review pass focused on the fast-element modularization, hydration opt-in path, packaging, and docs.
- Update css imports from '@microsoft/fast-element' to '@microsoft/fast-element/styles.js' across 5 doc files - Replace ElementController.configHydration() with enableHydration() - Replace ElementHydrationCallbacks with HydrationOptions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- enableHydration() now calls ensureDeclarativeRuntime() so html templates become hydratable (#1) - TemplateElement.config() enables hydration when per-element callbacks (elementWillHydrate/elementDidHydrate) are configured (#2) - ElementStyles lazily initializes DefaultStyleStrategy so styles.js subpath works standalone without element-controller (#3) - Disable dtsRollup in all subpath api-extractor configs (#4) - Re-export moved types (ElementStyles, HostBehavior, etc.) as type exports from root barrel (#5) - Add api-extractor configs for styles.js and arrays.js subpaths (#6) - Update 3.x docs to import css from styles.js subpath (#7) - Fix migration guide New Exports table with current APIs (#8) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename ensureDeclarativeRuntime to ensureHydrationRuntime and move to src/hydration/runtime.ts. enableHydration() now imports from the hydration module directly, with no dependency on declarative/. declarative/runtime.ts becomes a thin wrapper that calls ensureHydrationRuntime() + registers declarative debug messages. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu
left a comment
There was a problem hiding this comment.
Re-review follow-up: the original hydration runtime/config, standalone styles, dts rollup, subpath API extraction, and website migration table fixes are mostly addressed. I found a few remaining API/doc edge cases. One non-inline note because the file is not changed in this PR diff: packages/fast-element/MIGRATION.md still points HydrationControllerCallbacks to ElementHydrationCallbacks via ElementController.configHydration() and still says the install-hydration import has no replacement; that should be updated to the new enableHydration() / HydrationOptions migration path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Make enableHydration() idempotent — first call wins, subsequent calls are no-ops so TemplateElement.config() shim can't drop previously registered callbacks - Add ConstructibleStyleStrategy type export to root barrel - Add Constructable, ExpressionController type exports to styles.js - Add Subscriber, SubscriberSet type exports to arrays.js - Fix migration guide 2.x example to show old root import - Fix css-templates.md prose to reference styles.js subpath Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu
left a comment
There was a problem hiding this comment.
Latest re-review: the previously resolved threads mostly check out, but I found a few remaining follow-ups. Non-inline note because this file is still not in the PR diff: packages/fast-element/MIGRATION.md still points HydrationControllerCallbacks to ElementHydrationCallbacks via ElementController.configHydration() and still says @microsoft/fast-element/install-hydration.js has no replacement; that guidance should be updated to the new enableHydration() / HydrationOptions migration path.
- Move hydrationEnabled guard above TSDoc so API Extractor attaches docs to enableHydration() correctly - Make enableHydration() merge-safe — hook installs once, subsequent calls chain their options into the shared HydrationTracker via mergeOptions() - Add ExecutionContext, SourceLifetime type exports to styles.js - Add Notifier type export to arrays.js Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu
left a comment
There was a problem hiding this comment.
Latest re-review: the hydration TSDoc, repeated enableHydration() registration, and styles/arrays API report exports are mostly fixed. Non-inline note because the file still is not part of the PR diff: packages/fast-element/MIGRATION.md still points HydrationControllerCallbacks to ElementHydrationCallbacks via ElementController.configHydration() and still says @microsoft/fast-element/install-hydration.js has no replacement; that guidance should be updated to the new enableHydration() / HydrationOptions migration path.
- chainCallback() now invokes each callback in its own try/catch
so a throwing first callback cannot suppress the second
- Replace {@link enableHydration} with plain text in declarative
TSDoc to avoid ae-unresolved-link warnings in doc:exports:ci
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
janechu
left a comment
There was a problem hiding this comment.
Latest re-review: the hydration callback chaining fix checks out and regression checks passed. One non-inline note remains because the file is still not part of this PR diff: packages/fast-element/MIGRATION.md still points HydrationControllerCallbacks to ElementHydrationCallbacks via ElementController.configHydration() and says @microsoft/fast-element/install-hydration.js has no replacement; that guidance should be updated to the new enableHydration() / HydrationOptions migration path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove ElementStyles, add observerMap and attributeMap measurements. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pull Request
📖 Description
Modularizes
@microsoft/fast-elementto reduce core bundle size and make hydration, styles, and array observation opt-in through subpath exports.Hydration is now opt-in —
enableHydration()from@microsoft/fast-element/hydration.jsactivates hydration via a pluggable hook. Without it,ElementControllerhas zero hydration imports or logic.New subpath exports split heavy features out of the main barrel:
./styles.js—css,ElementStyles,CSSDirective, style strategies./arrays.js—ArrayObserver,Splice,SpliceStrategy, array utilities./hydration.js—enableHydration,deferHydrationAttributeglobalThis.FASTremoved —FASTis now a module-scoped export with no side-effecting global mutation.FAST.getById(),FASTGlobal,KernelServiceId, and therequestIdleCallbackpolyfill are all removed.Per-element lifecycle callbacks are passed directly to
declarativeTemplate(), andisPrerendered/isHydratedare now separate promises with clear semantics.👩💻 Reviewer Notes
TemplateElement.config()is preserved as a deprecated shim for backward compatibility.ElementControllernow uses composition forPropertyChangeNotifierinstead of inheritance.renderPrerendered) moved entirely intoenable-hydration.tsviaElementController.installHydrationHook().isPrerenderedresolvestruewhen a DSD shadow root existed (independent of hydration).isHydratedresolvestrueonly when hydration actually ran.{{ }}bindings with spaces inside braces do not work — the templates.html files must use{{binding}}without spaces.📑 Test Plan
declarative-no-hydrationfixture (5 tests) verifies client-side rendering, lifecycle callbacks without hydration, andisPrerendered/isHydratedsemanticsplatform.pw.spec.tsanddebug.pw.spec.tsfor module-scoped FASTlifecycle-callbacksandperformance-metricsfixtures for new API✅ Checklist
General
$ npm run change⏭ Next Steps
TemplateElement.config()deprecated shim in a future releaseCompileron first render for further size reduction