diff --git a/packages/web-components/fast-ssr/README.md b/packages/web-components/fast-ssr/README.md
index a9c1008f42d..951f6a8224b 100644
--- a/packages/web-components/fast-ssr/README.md
+++ b/packages/web-components/fast-ssr/README.md
@@ -3,22 +3,20 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![npm version](https://badge.fury.io/js/%40microsoft%2Ffast-ssr.svg)](https://badge.fury.io/js/%40microsoft%2Ffast-ssr)
-The `@microsoft/fast-ssr` package contains a NodeJS solution for emitting templates and components built using FAST to HTML markup strings.
+The `@microsoft/fast-ssr` package contains a NodeJS solution for rendering FAST templates and components. While primarily intended for supporting SSR scenarios, it also allows FAST to be used as a general purpose HTML templating solution.
## Requirements
- [NodeJS ^16.0.0](https://nodejs.org)
- [`@microsoft/fast-element@^2.0.0`](https://www.npmjs.com/package/@microsoft/fast-element)
## Important Notes
-`@microsoft/fast-ssr` is built using ES modules. This documentation assumes all JavaScript is run as ES module scripts. If that is not the case in your project, you can force ES module format by converting `.js` extensions to `.mjs`. [Click here for more information.](https://nodejs.org/api/packages.html)
+`@microsoft/fast-ssr` is built using ES modules. This documentation assumes all JavaScript is run as ES module scripts. If that is not the case in your project, you can force ES module format by converting `.js` extensions to `.mjs`. [Details on this here.](https://nodejs.org/api/packages.html)
-## How it Works
-`@microsoft/fast-ssr` internally implements `ElementRenderer` interface proposed [here](https://github.com/webcomponents-cg/community-protocols/issues/7#issuecomment-825151215)
-nd implemented in [`@lit-labs/ssr`](https://github.com/lit/lit/tree/main/packages/labs/ssr). The `ElementRenderer` interface allows Custom Elements created by different component authoring libraries to be rendered side-by-side, maximizing the portability and interoperability of Web Components.
+### Design Philosophy
+Performance is a core tenant of this package. To help achieve that, it avoids needing a comprehensive DOM implementation to render components, instead relying on a minimal set of DOM globals provided by the packaged DOM shim. Doing so comes with an important idea to understand; non-custom elements (`
`, `
`, etc) are never instantiated as JavaScript objects and you cannot gain references to these elements declared in a template. The parts of a template containing these elements are emitted as-is to the SSR string result (with any bindings evaluated). It is for this reason [certain directives are not no-ops](#directive-support-matrix). Additionally, components are rendered using only their template. Imperative code that adds elements or DOM content to a component will be invisible to the template renderer and will not be yielded in the final string result.
-- Parses string templates to HTML, depth-first traversal, constructing custom element nodes it finds and yielding out shadow dom.
-- Only custom elements are constructed, native elements are yielded directly as strings. This means directives such as `children`, `slotted`, and `ref` are no-ops.
-- Elements need to exist in the template to be rendered. Idiomatic operations appending and removing elements will not result in SSR string changes
+## Community Alignment
+`@microsoft/fast-ssr` internally implements `ElementRenderer` interface proposed [here](https://github.com/webcomponents-cg/community-protocols/issues/7#issuecomment-825151215) and implemented in [`@lit-labs/ssr`](https://github.com/lit/lit/tree/main/packages/labs/ssr). The `ElementRenderer` interface allows Custom Elements created by different component authoring libraries to be rendered side-by-side, maximizing the portability and interoperability of Web Components. For more on how to do that, see the section on [configuring the RenderInfo object](#configuring-the-renderinfo-object).
## Installation
Install `@microsoft/fast-ssr` and `@microsoft/fast-element` using your package manager of choice:
@@ -37,7 +35,7 @@ import "@microsoft/fast-ssr/install-dom-shim";
Alternatively, a full DOM implementation such as [`jsdom`](https://github.com/jsdom/jsdom) or [`happy-dom`](https://github.com/capricorn86/happy-dom) can be used.
### Construct the Renderer
-Import the renderer factory and construct a `TemplateRenderer`:
+Import the renderer factory and construct a `TemplateRenderer`. You will also need a `RenderInfo` object and a default is provided, more on this [here](#configuring-the-renderinfo-object).
```js
import fastSSR from "@microsoft/fast-ssr";
@@ -60,25 +58,31 @@ class MyElement extends FASTElement {
With the `TemplateRenderer` created and a custom element defined, the `TemplateRenderer` can now render a template with that element. The result will be an `iterableIterator`, which allows the result to be streamed to the client before the entire template has been rendered.
```js
-const result templateRenderer.render(html`
-
-
-
-
-
-
+const result = templateRenderer.render(html`
+
+
+
+
+
+
`, defaultRenderInfo);
```
+The template being rendered can also be a fragment of HTML, it does not need to be a valid document:
+
+```js
+const result = templateRenderer.render(html``, defaultRenderInfo);
+```
+
#### Rendering Strings
The template renderer can also render `string` types just as it would a template:
```js
-const result templateRenderer.render("", defaultRenderInfo);
+const result = templateRenderer.render("", defaultRenderInfo);
```
#### Rendering Templates with Bindings
-A template can be rendered with arbitrary source data. When done so, bindings are invoked with that source data:
+A template can be rendered with arbitrary source data by providing that source as the third argument to `.render()`. When done so, bindings are invoked with that source data:
```ts
const result = templateRenderer.render(html`
@@ -87,9 +91,60 @@ const result = templateRenderer.render(html`
```
#### Rendering Templates with Directives
-The `TemplateRenderer` can render templates with `@microsoft/fast-element` directives such as `when` and `repeat`.
+The `TemplateRenderer` can render templates with directives such as `when` and `repeat`. Some directives are no-ops, see the [directive support matrix](#directive-support-matrix) for details.
+
+```ts
+import { when, repeat } from "@microsoft/fast-element";
+
+const result = templateRenderer.render(html`
+ ${when(x => x.shouldRender, html`
+ Colors of a pixel
+ ${repeat(x => x.data, html`${color => color}`)}
+ `)}
+`, defaultRenderInfo, { shouldRender: true, data: ["red", "green", "blue"] });
+```
+
+##### Directive Support Matrix
+
+|Directive Name|Support|
+|-|-|
+|[`children`](https://www.fast.design/docs/fast-element/using-directives#the-children-directive)|[![](https://img.shields.io/badge/-Unsupported-red)]()|
+|[`ref`](https://www.fast.design/docs/fast-element/using-directives#the-ref-directive)|[![](https://img.shields.io/badge/-Unsupported-red)]()|
+|[`repeat`](https://www.fast.design/docs/fast-element/using-directives#the-repeat-directive)|[![](https://img.shields.io/badge/-Supported-brightgreen)]()|
+|[`slotted`](https://www.fast.design/docs/fast-element/using-directives#the-slotted-directive)|[![](https://img.shields.io/badge/-Unsupported-red)]()|
+|[`when`](https://www.fast.design/docs/fast-element/using-directives#the-when-directive)|[![](https://img.shields.io/badge/-Supported-brightgreen)]()|
-## Testing
-This package uses Playwright and a lightweight web server for running tests. You can run the tests by running `npm run test`.
+> Unsupported directives are no-ops. To understand more about why, see the [Design Philosophy.](#design-philosophy)
-> Playwright may prompt you to install browsers to run the tests. If so, follow the instructions provided or run `npm run install-playwright-browsers`.
\ No newline at end of file
+### Configuring the RenderInfo Object
+`TemplateRenderer.render()` must be invoked with a `RenderInfo` object. It's purpose is to provide different element renderers to the process, as well as metadata about the rendering process. It can be used to render custom elements from different templating libraries in the same process. A pre-generated object is created for you by the factory function, but you can also easily construct your own:
+
+```js
+const { templateRenderer, elementRenderer } = fastSSR();
+templateRenderer.render(html``, {
+ elementRenderers: [elementRenderer],
+ customElementHostStack: [],
+ customElementInstanceStack: [],
+});
+```
+
+To render elements built with another library, that library will need to provide an `ElementRenderer` for the library. For example, to render a Lit element along-side a FAST element, provide the FAST `ElementRenderer` *and* the Lit `ElementRenderer` to the `RenderInfo` object:
+
+```js
+import fastSSR from "@microsoft/fast-ssr";
+import { html } from "@microsoft/fast-element";
+import { LitElementRenderer } from "@lit-labs/ssr/lib/lit-element-renderer.js"
+
+const { templateRenderer, elementRenderer } = fastSSR();
+// Some implementation that defines both FAST and Lit components
+defineComponents();
+
+const result = templateRenderer.render(html`
+
+
+`, {
+ elementRenderers: [elementRenderer, LitElementRenderer],
+ customElementHostStack: [],
+ customElementInstanceStack: []
+});
+```
diff --git a/packages/web-components/fast-ssr/docs/api-report.md b/packages/web-components/fast-ssr/docs/api-report.md
index 51c8333c316..208614dfdf7 100644
--- a/packages/web-components/fast-ssr/docs/api-report.md
+++ b/packages/web-components/fast-ssr/docs/api-report.md
@@ -51,12 +51,20 @@ export class TemplateRenderer {
//
// @internal
renderOpCodes(codes: Op[], renderInfo: RenderInfo, source: unknown, context: ExecutionContext): IterableIterator;
- // Warning: (ae-forgotten-export) The symbol "ViewBehaviorFactoryRenderer" needs to be exported by the entry point exports.d.ts
- //
// @internal
withViewBehaviorFactoryRenderers(...renderers: ViewBehaviorFactoryRenderer[]): void;
}
+// @public
+export interface ViewBehaviorFactoryRenderer {
+ // (undocumented)
+ matcher: Constructable;
+ // Warning: (ae-incompatible-release-tags) The symbol "render" is marked as @public, but its signature references "TemplateRenderer" which is marked as @beta
+ //
+ // (undocumented)
+ render(behavior: T, renderInfo: RenderInfo, source: any, renderer: TemplateRenderer, context: ExecutionContext): IterableIterator;
+}
+
// (No @packageDocumentation comment for this package)
```
diff --git a/packages/web-components/fast-ssr/src/exports.ts b/packages/web-components/fast-ssr/src/exports.ts
index 5ca3b65dde9..78b653b71e5 100644
--- a/packages/web-components/fast-ssr/src/exports.ts
+++ b/packages/web-components/fast-ssr/src/exports.ts
@@ -8,7 +8,10 @@ import {
import { FASTElementRenderer } from "./element-renderer/element-renderer.js";
import { FASTSSRStyleStrategy } from "./element-renderer/style-strategy.js";
import { StyleElementStyleRenderer, StyleRenderer } from "./styles/style-renderer.js";
-import { defaultViewBehaviorFactoryRenderers } from "./template-renderer/directives.js";
+import {
+ defaultViewBehaviorFactoryRenderers,
+ ViewBehaviorFactoryRenderer,
+} from "./template-renderer/directives.js";
import {
ComponentDOMEmissionMode,
TemplateRenderer,
@@ -74,8 +77,9 @@ export default function fastSSR(): {
}
export type {
- TemplateRenderer,
+ ComponentDOMEmissionMode,
FASTElementRenderer,
StyleRenderer,
- ComponentDOMEmissionMode,
+ TemplateRenderer,
+ ViewBehaviorFactoryRenderer,
};
diff --git a/packages/web-components/fast-ssr/src/template-renderer/directives.ts b/packages/web-components/fast-ssr/src/template-renderer/directives.ts
index 59ab38fccba..9bb76f1f3da 100644
--- a/packages/web-components/fast-ssr/src/template-renderer/directives.ts
+++ b/packages/web-components/fast-ssr/src/template-renderer/directives.ts
@@ -13,7 +13,9 @@ import {
import { TemplateRenderer } from "./template-renderer.js";
/**
- * Describes an implementation that can render a directive
+ * Describes an implementation that can render a directive.
+ *
+ * @public
*/
export interface ViewBehaviorFactoryRenderer {
render(
@@ -26,8 +28,8 @@ export interface ViewBehaviorFactoryRenderer {
matcher: Constructable;
}
-export const RepeatDirectiveRenderer: ViewBehaviorFactoryRenderer = Object.freeze(
- {
+export const RepeatDirectiveRenderer: ViewBehaviorFactoryRenderer =
+ Object.freeze({
matcher: RepeatDirective,
*render(
directive: RepeatDirective,
@@ -64,31 +66,27 @@ export const RepeatDirectiveRenderer: ViewBehaviorFactoryRenderer = Object.freeze(
- {
+export const ChildrenDirectiveRenderer: ViewBehaviorFactoryRenderer =
+ Object.freeze({
matcher: ChildrenDirective,
render: noop,
- }
-);
+ });
-export const RefDirectiveRenderer: ViewBehaviorFactoryRenderer = Object.freeze(
- {
+export const RefDirectiveRenderer: ViewBehaviorFactoryRenderer =
+ Object.freeze({
matcher: RefDirective,
render: noop,
- }
-);
-export const SlottedDirectiveRenderer: ViewBehaviorFactoryRenderer = Object.freeze(
- {
+ });
+export const SlottedDirectiveRenderer: ViewBehaviorFactoryRenderer =
+ Object.freeze({
matcher: SlottedDirective,
render: noop,
- }
-);
+ });
export const defaultViewBehaviorFactoryRenderers: ViewBehaviorFactoryRenderer[] = [
RepeatDirectiveRenderer,