Skip to content

feat: modularize FASTElement with opt-in hydration and subpath exports#7497

Merged
janechu merged 19 commits intoreleases/fast-element-v3from
users/janechu/expose-hydration-callbacks
Apr 25, 2026
Merged

feat: modularize FASTElement with opt-in hydration and subpath exports#7497
janechu merged 19 commits intoreleases/fast-element-v3from
users/janechu/expose-hydration-callbacks

Conversation

@janechu
Copy link
Copy Markdown
Collaborator

@janechu janechu commented Apr 24, 2026

Pull Request

📖 Description

Modularizes @microsoft/fast-element to reduce core bundle size and make hydration, styles, and array observation opt-in through subpath exports.

Hydration is now opt-inenableHydration() from @microsoft/fast-element/hydration.js activates hydration via a pluggable hook. Without it, ElementController has zero hydration imports or logic.

New subpath exports split heavy features out of the main barrel:

  • ./styles.jscss, ElementStyles, CSSDirective, style strategies
  • ./arrays.jsArrayObserver, Splice, SpliceStrategy, array utilities
  • ./hydration.jsenableHydration, deferHydrationAttribute

globalThis.FAST removedFAST is now a module-scoped export with no side-effecting global mutation. FAST.getById(), FASTGlobal, KernelServiceId, and the requestIdleCallback polyfill are all removed.

Per-element lifecycle callbacks are passed directly to declarativeTemplate(), and isPrerendered / isHydrated are now separate promises with clear semantics.

👩‍💻 Reviewer Notes

  • TemplateElement.config() is preserved as a deprecated shim for backward compatibility.
  • ElementController now uses composition for PropertyChangeNotifier instead of inheritance.
  • The hydration rendering path (renderPrerendered) moved entirely into enable-hydration.ts via ElementController.installHydrationHook().
  • isPrerendered resolves true when a DSD shadow root existed (independent of hydration). isHydrated resolves true only when hydration actually ran.
  • Declarative template {{ }} bindings with spaces inside braces do not work — the templates.html files must use {{binding}} without spaces.

📑 Test Plan

  • 1396 Playwright tests pass (chromium)
  • 176 declarative fixture tests pass (chromium)
  • New declarative-no-hydration fixture (5 tests) verifies client-side rendering, lifecycle callbacks without hydration, and isPrerendered/isHydrated semantics
  • Updated platform.pw.spec.ts and debug.pw.spec.ts for module-scoped FAST
  • Updated lifecycle-callbacks and performance-metrics fixtures for new API

✅ Checklist

General

  • I have included a change request file using $ npm run change
  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

⏭ Next Steps

  • Run full CI across all browsers (Firefox, WebKit)
  • Remove TemplateElement.config() deprecated shim in a future release
  • Consider lazy-loading the Compiler on first render for further size reduction

janechu and others added 7 commits April 24, 2026 15:42
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>
@janechu janechu changed the title feat: modularize hydration and expose lifecycle callbacks feat: modularize FASTElement with opt-in hydration and subpath exports Apr 25, 2026
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>
@janechu janechu marked this pull request as ready for review April 25, 2026 00:32
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator Author

@janechu janechu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep review pass focused on the fast-element modularization, hydration opt-in path, packaging, and docs.

Comment thread packages/fast-element/src/components/enable-hydration.ts
Comment thread packages/fast-element/src/declarative/template.ts Outdated
Comment thread packages/fast-element/src/styles.ts
Comment thread packages/fast-element/api-extractor.hydration.json
Comment thread packages/fast-element/docs/api-report.api.md Outdated
Comment thread packages/fast-element/scripts/run-api-extractor.js
Comment thread sites/website/src/docs/3.x/migration-guide.md
Comment thread sites/website/src/docs/3.x/migration-guide.md
janechu and others added 4 commits April 24, 2026 18:17
- 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>
Copy link
Copy Markdown
Collaborator Author

@janechu janechu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/fast-element/src/components/enable-hydration.ts Outdated
Comment thread packages/fast-element/src/index.ts Outdated
Comment thread packages/fast-element/docs/styles/api-report.api.md Outdated
Comment thread packages/fast-element/docs/arrays/api-report.api.md Outdated
Comment thread sites/website/src/docs/3.x/migration-guide.md Outdated
Comment thread sites/website/src/docs/3.x/getting-started/css-templates.md
janechu and others added 2 commits April 24, 2026 19:39
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>
Copy link
Copy Markdown
Collaborator Author

@janechu janechu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/fast-element/src/components/enable-hydration.ts Outdated
Comment thread packages/fast-element/src/components/enable-hydration.ts Outdated
Comment thread packages/fast-element/src/styles.ts Outdated
Comment thread packages/fast-element/src/arrays.ts Outdated
- 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>
Copy link
Copy Markdown
Collaborator Author

@janechu janechu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/fast-element/src/components/hydration-tracker.ts Outdated
Comment thread packages/fast-element/src/declarative/template.ts Outdated
- 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>
Copy link
Copy Markdown
Collaborator Author

@janechu janechu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/fast-element/src/declarative/template.ts Outdated
janechu and others added 2 commits April 24, 2026 20:58
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>
@janechu janechu merged commit b78786a into releases/fast-element-v3 Apr 25, 2026
11 checks passed
@janechu janechu deleted the users/janechu/expose-hydration-callbacks branch April 25, 2026 04:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant