Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@lit-labs/ssr

Package Overview
Dependencies
Maintainers
10
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lit-labs/ssr - npm Package Compare versions

Comparing version 3.0.1 to 3.1.0

77

lib/element-renderer.d.ts
/// <reference lib="dom" />
import type { RenderInfo } from './render-value.js';
import type { RenderResult } from './render-result.js';
export declare type Constructor<T> = {
new (): T;
declare type Interface<T> = {
[P in keyof T]: T[P];
};
export declare type ElementRendererConstructor = (new (tagName: string) => ElementRenderer) & typeof ElementRenderer;
export declare type ElementRendererConstructor = (new (tagName: string) => Interface<ElementRenderer>) & typeof ElementRenderer;
declare type AttributesMap = Map<string, string>;
export declare const getElementRenderer: ({ elementRenderers }: RenderInfo, tagName: string, ceClass?: typeof HTMLElement | undefined, attributes?: AttributesMap) => ElementRenderer;
export interface ShadowRootOptions {
mode: 'open' | 'closed';
delegatesFocus?: boolean;
}
/**
* @deprecated Use ShadowRootInit instead
*/
export declare type ShadowRootOptions = ShadowRootInit;
/**
* An object that renders elements of a certain type.

@@ -30,15 +30,35 @@ */

static matchesClass(_ceClass: typeof HTMLElement, _tagName: string, _attributes: AttributesMap): boolean;
/**
* Called when a custom element is instantiated during a server render.
*
* An ElementRenderer can actually instantiate the custom element class, or
* it could emulate the element in some other way.
*/
constructor(tagName: string);
/**
* Should implement server-appropriate implementation of connectedCallback
* Called when a custom element is "attached" to the server DOM.
*
* Because we don't presume a full DOM emulation, this isn't the same as
* being connected in a real browser. There may not be an owner document,
* parentNode, etc., depending on the DOM emulation.
*
* If this renderer is creating actual element instances, it may forward
* the call to the element's `connectedCallback()`.
*
* The default impementation is a no-op.
*/
abstract connectedCallback(): void;
connectedCallback(): void;
/**
* Should implement server-appropriate implementation of attributeChangedCallback
* Called from `setAttribute()` to emulate the browser's
* `attributeChangedCallback` lifecycle hook.
*
* If this renderer is creating actual element instances, it may forward
* the call to the element's `attributeChangedCallback()`.
*/
abstract attributeChangedCallback(name: string, old: string | null, value: string | null): void;
attributeChangedCallback(_name: string, _old: string | null, _value: string | null): void;
/**
* Handles setting a property.
* Handles setting a property on the element.
*
* Default implementation sets the property on the renderer's element instance.
* The default implementation sets the property on the renderer's element
* instance.
*

@@ -61,22 +81,35 @@ * @param name Name of the property

/**
* Override this getter to configure the element's shadow root, if one is
* created with `renderShadow`.
* The shadow root options to write to the declarative shadow DOM <template>,
* if one is created with `renderShadow()`.
*/
get shadowRootOptions(): ShadowRootOptions;
get shadowRootOptions(): ShadowRootInit;
/**
* Render a single element's ShadowRoot children.
* Render the element's shadow root children.
*
* If `renderShadow()` returns undefined, no declarative shadow root is
* emitted.
*/
abstract renderShadow(_renderInfo: RenderInfo): RenderResult | undefined;
renderShadow(_renderInfo: RenderInfo): RenderResult | undefined;
/**
* Render an element's light DOM children.
* Render the element's light DOM children.
*/
abstract renderLight(renderInfo: RenderInfo): RenderResult | undefined;
renderLight(_renderInfo: RenderInfo): RenderResult | undefined;
/**
* Render an element's attributes.
* Render the element's attributes.
*
* Default implementation serializes all attributes on the element instance.
* The default implementation serializes all attributes on the element
* instance.
*/
renderAttributes(): RenderResult;
}
/**
* An ElementRenderer used as a fallback in the case where a custom element is
* either unregistered or has no other matching renderer.
*/
export declare class FallbackRenderer extends ElementRenderer {
private readonly _attributes;
setAttribute(name: string, value: string): void;
renderAttributes(): RenderResult;
}
export {};
//# sourceMappingURL=element-renderer.d.ts.map

@@ -29,2 +29,8 @@ /// <reference lib="dom" />

export class ElementRenderer {
/**
* Called when a custom element is instantiated during a server render.
*
* An ElementRenderer can actually instantiate the custom element class, or
* it could emulate the element in some other way.
*/
constructor(tagName) {

@@ -46,6 +52,32 @@ this.tagName = tagName;

/**
* Handles setting a property.
* Called when a custom element is "attached" to the server DOM.
*
* Default implementation sets the property on the renderer's element instance.
* Because we don't presume a full DOM emulation, this isn't the same as
* being connected in a real browser. There may not be an owner document,
* parentNode, etc., depending on the DOM emulation.
*
* If this renderer is creating actual element instances, it may forward
* the call to the element's `connectedCallback()`.
*
* The default impementation is a no-op.
*/
connectedCallback() {
// do nothing
}
/**
* Called from `setAttribute()` to emulate the browser's
* `attributeChangedCallback` lifecycle hook.
*
* If this renderer is creating actual element instances, it may forward
* the call to the element's `attributeChangedCallback()`.
*/
attributeChangedCallback(_name, _old, _value) {
// do nothing
}
/**
* Handles setting a property on the element.
*
* The default implementation sets the property on the renderer's element
* instance.
*
* @param name Name of the property

@@ -78,4 +110,4 @@ * @param value Value of the property

/**
* Override this getter to configure the element's shadow root, if one is
* created with `renderShadow`.
* The shadow root options to write to the declarative shadow DOM <template>,
* if one is created with `renderShadow()`.
*/

@@ -86,6 +118,22 @@ get shadowRootOptions() {

/**
* Render an element's attributes.
* Render the element's shadow root children.
*
* Default implementation serializes all attributes on the element instance.
* If `renderShadow()` returns undefined, no declarative shadow root is
* emitted.
*/
renderShadow(_renderInfo) {
return undefined;
}
/**
* Render the element's light DOM children.
*/
renderLight(_renderInfo) {
return undefined;
}
/**
* Render the element's attributes.
*
* The default implementation serializes all attributes on the element
* instance.
*/
*renderAttributes() {

@@ -109,3 +157,3 @@ if (this.element !== undefined) {

*/
class FallbackRenderer extends ElementRenderer {
export class FallbackRenderer extends ElementRenderer {
constructor() {

@@ -128,7 +176,3 @@ super(...arguments);

}
connectedCallback() { }
attributeChangedCallback() { }
*renderLight() { }
*renderShadow() { }
}
//# sourceMappingURL=element-renderer.js.map

@@ -9,2 +9,3 @@ /**

import { _$LE } from 'lit-element/private-ssr-support.js';
import { ariaMixinAttributes, HYDRATE_INTERNALS_ATTR_PREFIX, } from '@lit-labs/ssr-dom-shim';
import { renderValue } from './render-value.js';

@@ -19,2 +20,17 @@ const { attributeToProperty, changedProperties } = _$LE;

this.element = new (customElements.get(this.tagName))();
// Reflect internals AOM attributes back to the DOM prior to hydration to
// ensure search bots can accurately parse element semantics prior to
// hydration. This is called whenever an instance of ElementInternals is
// created on an element to wire up the getters/setters for the ARIAMixin
// properties.
const internals = this.element.__internals;
if (internals) {
for (const [ariaProp, ariaAttribute] of Object.entries(ariaMixinAttributes)) {
const value = internals[ariaProp];
if (value && !this.element.hasAttribute(ariaAttribute)) {
this.element.setAttribute(ariaAttribute, value);
this.element.setAttribute(`${HYDRATE_INTERNALS_ATTR_PREFIX}${ariaAttribute}`, value);
}
}
}
}

@@ -21,0 +37,0 @@ static matchesClass(ctor) {

@@ -45,3 +45,3 @@ /**

* Most of the hooks implement fairly standard web-compatible module loading:
* - An import specifier resolver that uses Node module resoution
* - An import specifier resolver that uses Node module resolution
* - A linker that loads dependencies from the local filesystem

@@ -64,3 +64,3 @@ * - A module cache keyed by resolved URL

*
* The keys of the map are useful for enumering static imported modules
* The keys of the map are useful for enumerating static imported modules
* after an entrypoint is loaded.

@@ -67,0 +67,0 @@ */

@@ -102,3 +102,3 @@ /**

* Most of the hooks implement fairly standard web-compatible module loading:
* - An import specifier resolver that uses Node module resoution
* - An import specifier resolver that uses Node module resolution
* - A linker that loads dependencies from the local filesystem

@@ -124,3 +124,3 @@ * - A module cache keyed by resolved URL

*
* The keys of the map are useful for enumering static imported modules
* The keys of the map are useful for enumerating static imported modules
* after an entrypoint is loaded.

@@ -127,0 +127,0 @@ */

@@ -22,3 +22,3 @@ /**

*
* Commented elements were emperically found to not reflect from an associated
* Commented elements were empirically found to not reflect from an associated
* property.

@@ -25,0 +25,0 @@ */

@@ -23,3 +23,3 @@ /// <reference lib="dom" />

/**
* An optional callback to notifiy when a custom element has been rendered.
* An optional callback to notify when a custom element has been rendered.
*

@@ -26,0 +26,0 @@ * This allows servers to know what specific tags were rendered for a given

@@ -66,3 +66,7 @@ /// <reference lib="dom" />

* - `text`
* - Emit run of static text: `<div><span>Hello</span><span`
* - Emit run of static text: `<div><span>Hello</span>`
* - `possible-node-marker`
* - Emit `<!--lit-node n-->` marker since there are attribute parts
* - `text`
* - Emit run of static text: `<span`
* - `attribute-part`

@@ -73,4 +77,4 @@ * - Emit an AttributePart's value, e.g. ` class="bold"`

* - `child-part`
* - Emit the ChildPart's value, in this case a TemplateResult, thus we recurse
* into that template's opcodes
* - Emit the ChildPart's value, in this case a TemplateResult, thus we
* recurse into that template's opcodes
* - `text`

@@ -85,2 +89,5 @@ * - Emit run of static text: `/span></div>`

*
* - `possible-node-marker`
* - Emit `<!--lit-node n-->` marker since there are attribute parts and we
* may emit the `defer-hydration` attribute on the node that follows
* - `text`

@@ -101,5 +108,2 @@ * - Emit open tag `<x-foo`

* - Emit end of of open tag `>`
* - `possible-node-marker`
* - Emit `<!--lit-node n-->` marker if there were attribute parts or
* we needed to emit the `defer-hydration` attribute
* - `custom-element-shadow`

@@ -154,3 +158,3 @@ * - Emit `renderer.renderShadow()` (emits `<template shadowroot>` +

* opcode (if already `text`) or by creating a new `text` opcode (if the
* previous opocde was not `text)
* previous opcode was not `text)
*/

@@ -200,16 +204,9 @@ const flush = (value) => {

else if (isElementNode(node)) {
// Whether to flush the start tag. This is necessary if we're changing
// any of the attributes in the tag, so it's true for custom-elements
// which might reflect their own state, or any element with a binding.
let writeTag = false;
let boundAttributesCount = 0;
const tagName = node.tagName;
let ctor;
if (tagName.indexOf('-') !== -1) {
// Looking up the constructor here means that custom elements must be
// registered before rendering the first template that contains them.
ctor = customElements.get(tagName);
const ctor = customElements.get(tagName);
if (ctor !== undefined) {
// Write the start tag
writeTag = true;
// Mark that this is a custom element

@@ -228,2 +225,3 @@ node.isDefinedCustomElement = true;

if (node.attrs.length > 0) {
const attrInfo = [];
for (const attr of node.attrs) {

@@ -233,4 +231,24 @@ const isAttrBinding = attr.name.endsWith(boundAttributeSuffix);

if (isAttrBinding || isElementBinding) {
writeTag = true;
boundAttributesCount += 1;
}
attrInfo.push([isAttrBinding, isElementBinding, attr]);
}
if (boundAttributesCount > 0 || node.isDefinedCustomElement) {
// We (may) need to emit a `<!-- lit-node -->` comment marker to
// indicate the following node needs to be identified during
// hydration when it has bindings or if it is a custom element (and
// thus may need its `defer-hydration` to be removed, depending on
// the `deferHydration` setting). The marker is emitted as a
// previous sibling before the node in question, to avoid issues
// with void elements (which do not have children) and raw text
// elements (whose children are intepreted as text).
flushTo(node.sourceCodeLocation.startTag.startOffset);
ops.push({
type: 'possible-node-marker',
boundAttributesCount,
nodeIndex,
});
}
for (const [isAttrBinding, isElementBinding, attr] of attrInfo) {
if (isAttrBinding || isElementBinding) {
// Note that although we emit a lit-node comment marker for any

@@ -263,3 +281,3 @@ // nodes with bindings, we don't account for it in the nodeIndex because

tagName: tagName.toUpperCase(),
useCustomElementInstance: ctor !== undefined,
useCustomElementInstance: node.isDefinedCustomElement,
});

@@ -287,21 +305,12 @@ }

}
if (writeTag) {
if (node.isDefinedCustomElement) {
flushTo(node.sourceCodeLocation.startTag.endOffset - 1);
ops.push({
type: 'custom-element-attributes',
});
flush('>');
skipTo(node.sourceCodeLocation.startTag.endOffset);
}
else {
flushTo(node.sourceCodeLocation.startTag.endOffset);
}
if (node.isDefinedCustomElement) {
// For custom elements, add an opcode to write out attributes,
// close the tag, and then add an opcode to write the shadow
// root
flushTo(node.sourceCodeLocation.startTag.endOffset - 1);
ops.push({
type: 'possible-node-marker',
boundAttributesCount,
nodeIndex,
type: 'custom-element-attributes',
});
}
if (ctor !== undefined) {
flush('>');
skipTo(node.sourceCodeLocation.startTag.endOffset);
ops.push({

@@ -485,20 +494,18 @@ type: 'custom-element-shadow',

}
if (instance.renderShadow !== undefined) {
renderInfo.customElementHostStack.push(instance);
const shadowContents = instance.renderShadow(renderInfo);
// Only emit a DSR if renderShadow() emitted something (returning
// undefined allows effectively no-op rendering the element)
if (shadowContents !== undefined) {
const { mode = 'open', delegatesFocus } = instance.shadowRootOptions ?? {};
// `delegatesFocus` is intentionally allowed to coerce to boolean to
// match web platform behavior.
const delegatesfocusAttr = delegatesFocus
? ' shadowrootdelegatesfocus'
: '';
yield `<template shadowroot="${mode}"${delegatesfocusAttr}>`;
yield* shadowContents;
yield '</template>';
}
renderInfo.customElementHostStack.pop();
renderInfo.customElementHostStack.push(instance);
const shadowContents = instance.renderShadow(renderInfo);
// Only emit a DSR if renderShadow() emitted something (returning
// undefined allows effectively no-op rendering the element)
if (shadowContents !== undefined) {
const { mode = 'open', delegatesFocus } = instance.shadowRootOptions ?? {};
// `delegatesFocus` is intentionally allowed to coerce to boolean to
// match web platform behavior.
const delegatesfocusAttr = delegatesFocus
? ' shadowrootdelegatesfocus'
: '';
yield `<template shadowroot="${mode}" shadowrootmode="${mode}"${delegatesfocusAttr}>`;
yield* shadowContents;
yield '</template>';
}
renderInfo.customElementHostStack.pop();
break;

@@ -505,0 +512,0 @@ }

@@ -19,3 +19,3 @@ /**

* @param renderInfo Optional render context object that should be passed
* to any re-entrant calls to `render`, e.g. from a `renderShadow` callback
* to any reentrant calls to `render`, e.g. from a `renderShadow` callback
* on an ElementRenderer.

@@ -22,0 +22,0 @@ */

@@ -17,3 +17,3 @@ /**

* @param renderInfo Optional render context object that should be passed
* to any re-entrant calls to `render`, e.g. from a `renderShadow` callback
* to any reentrant calls to `render`, e.g. from a `renderShadow` callback
* on an ElementRenderer.

@@ -20,0 +20,0 @@ */

{
"name": "@lit-labs/ssr",
"type": "module",
"version": "3.0.1",
"version": "3.1.0",
"publishConfig": {

@@ -123,3 +123,6 @@ "access": "public"

"MODE": "prod",
"NODE_OPTIONS": "--experimental-vm-modules"
"NODE_OPTIONS": "--experimental-vm-modules",
"BROWSERS": {
"external": true
}
},

@@ -140,3 +143,6 @@ "dependencies": [

"MODE": "dev",
"NODE_OPTIONS": "--experimental-vm-modules"
"NODE_OPTIONS": "--experimental-vm-modules",
"BROWSERS": {
"external": true
}
},

@@ -207,3 +213,3 @@ "dependencies": [

"@lit-labs/ssr-client": "^1.0.0",
"@lit-labs/ssr-dom-shim": "^1.0.0",
"@lit-labs/ssr-dom-shim": "^1.1.0",
"@lit/reactive-element": "^1.6.0",

@@ -213,5 +219,5 @@ "@parse5/tools": "^0.1.0",

"enhanced-resolve": "^5.10.0",
"lit": "^2.6.0",
"lit-element": "^3.1.0",
"lit-html": "^2.6.0",
"lit": "^2.7.0",
"lit-element": "^3.3.0",
"lit-html": "^2.7.0",
"node-fetch": "^3.2.8",

@@ -218,0 +224,0 @@ "parse5": "^7.1.1"

@@ -75,6 +75,8 @@ # @lit-labs/ssr

### Hydrating lit templates
### Hydrating Lit templates
"Hydration" is the process of having lit re-associate the expressions of a lit template with the nodes they should update in the DOM. In order to "hydrate" lit templates, the `hydrate` method from the `experimental-hydrate` module is provided in the `lit` package. Prior to updating a server-rendered container using `render`, you should first call `hydrate` on that container using the same template and data that was used to render on the server:
"Hydration" is the process of re-associating expressions in a template with the nodes they should update in the DOM. Hydration is performed by the `hydrate()` function from the `lit/experimental-hydrate.js` module.
Prior to updating a server-rendered container using `render()`, you must first call `hydrate()` on that container using the same template and data that was used to render on the server:
```js

@@ -97,3 +99,3 @@ import {myTemplate} from './my-template.js';

Because the `hydrate` function above does not descend into shadow roots, it only works on one scope of the DOM at a time. To hydrate `LitElement` shadow roots, load the `lit/hydrate-support.js` module, which installs support for `LitElement` automatically hydrating itself when it detects it was server-rendered with declarative shadow DOM. This module should be loaded before the `lit` module is loaded, to ensure hydration support is properly installed.
`hydrate()` does not descend into shadow roots - it only works on one scope of the DOM at a time. To hydrate `LitElement` shadow roots, load the `lit/experimental-hydrate-support.js` module, which installs support for `LitElement` to automatically hydrate itself when it detects it was server-rendered with declarative shadow DOM. This module must be loaded before the `lit` module is loaded, to ensure hydration support is properly installed.

@@ -100,0 +102,0 @@ Put together, an HTML page that was server rendered and containing `LitElement`s in the main document might look like this:

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc