Socket
Socket
Sign inDemoInstall

lit-html

Package Overview
Dependencies
Maintainers
10
Versions
102
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

lit-html - npm Package Compare versions

Comparing version 2.0.0-pre.3 to 2.0.0-pre.4

development/directive-helpers.d.ts

384

CHANGELOG.md

@@ -20,158 +20,283 @@ # Change Log

<!-- ## [X.Y.Z] - YYYY-MM-DD -->
## [2.0.0-pre.4] - 2020-12-16
### Added
- `render` now returns the `NodePart` that was created/updated by `render`.
- Added `DisconnectableDirective`, which is a `Directive` subclass whose
`disconnectedCallback` will be called when the part containing the directive
is cleared (or transitively cleared by a Part higher in the tree) or manually
disconnected using the `setConnected` API, and whose `reconnectedCallback`
will be called when manually re-connected using `setConnected`. When
implementing `disconnectedCallback`, `reconnectedCallback` should also be
implemented to return the directive to a usable state. Note that `LitElement`
will disconnect directives upon element disconnection, and re-connect
directives upon element re-connection.
- Added `setConnected(isConnected: boolean)` to `NodePart`; when called with
`false`, `disconnectedCallback` will be run on any directives contained within
the part (directly or transitively), but without clearing or causing a
re-render to the tree. When called with `true`, any such directives'
`reconnectedCallback` will be called prior to its next `update`/`render`
callbacks. Note that `LitElement` will call this method by default on the
rendered part in its `connectedCallback` and `disconnetedCallback`.
- Added `unsafeStatic()`, which allows template authors to add strings to the
static structure of the template, before it's parsed as HTML.
- Added `isPrimitive()`, `isTemplateResult()`, and `isDirectiveResult()` to `lit-html/directive-helpers.js`.
### Changed
- [Breaking] Directives that asynchronously update their part value must now
extend `DisconnectableDirective` and call `this.setValue()`, a new API exposed
on the `DisconnectableDirective` class. Directives that render synchronously
to their `update` lifecycle should simply return the value to be committed to
their part from `update`/`render`.
- [Breaking] The `Directive` base class and `directive()` factory function are
now exported from the `lit-html/directive.js` module.
- [Breaking] (since 2.0.0-pre.3) The Part type constants (`NODE_PART`, etc) are
removed from the main `lit-html` module and exported as an enum-object named
`PartType` from `lit-html/directive.js`. Use `PartType.NODE` instead of
`NODE_TYPE`.
- [Breaking] (since 2.0.0-pre.3)) `lit-html/parts.js` has been renamed to
`lit-html/directive-helpers.js`.
- [Breaking] (since 2.0.0-pre.3)) Originally in `lit-html/parts.js`,
`createAndInsertPart()` and `insertPartBefore()` have been combined into a single `insertPart()` function in `lit-html/directive-helpers.js`. `detachNodePart()` and `restoreNodePart()` have been removed in favor of moving parts with `insertPart()`.
- [Breaking] (since 2.0.0-pre.3) `NodePart` has been renamed to `ChildPart`,
along with other methods and variables that use the "Node" naming, like
`PartType.Node` which is now `PartType.CHILD`.
- [Breaking] (since 2.0.0-pre.3) The `DirectiveClass`, `DirectiveParameters`
and `PartInfo` types have been moved from `lit-html` to
`lit-html/directive.ts`.
- [Breaking] (since 2.0.0-pre.3) The part exports (`ChildPart`,
`AttributePart`, etc) have been change to interface-only exports. The constructors are no longer exported. Directive authors should use helpers in `directive-helpers.js` to construct parts.
- [Breaking] (since 2.0.0-pre.3) The `setPartValue` function in
`directove-helpers.js` has been renamed to `setChildPartValue` and now only
supports ChildParts. Directives that require updating their container
part outside the `render`/`update` lifecycle should extend
`DisconnectableDirective` and use `this.setValue()`.
<!-- ### Fixed -->
<!-- ### Removed -->
## [2.0.0-pre.3] - 2020-09-21
### Changed
* [Breaking] `render()` no longer clears the container it's rendered to. It now appends to the container by default.
* [Breaking] Expressions in comments are not rendered or updated.
* [Breaking] Template caching happens per callsite, not per template-tag/callsize pair. This means some rare forms of highly dynamic template tags are no longer supported.
* [Breaking] Arrays and other iterables passed to attribute bindings are not specially handled. Arrays will be rendered with their default toString representation. This means that ```html`<div class=${['a', 'b']}>``` will render `<div class="a,b">` instead of `<div class="a b">`. To get the old behavior, use `array.join(' ')`.
* Multiple bindings in a single attribute value don't require the attribute value is quoted, as long as there is no whitespace or other attribute-ending character in the attribute value. ```html`<div id=${a}-${b}>```
* [Breaking] The directive and part APIs are significantly different. See the [README](README.md) for mroe details.
- [Breaking] The `shady-render` module has been removed and is now part of `platform-support`. There are also a couple of breaking changes. (1) Bindings in style elements are no longer supported. Previously these could not change and in the future they may be supported via static bindings. (2) `ShadyCSS.styleElement` is no longer called automatically. This must be called whenever dynamic changes that affect styling are made that involve css custom property shimming (older browsers) or changes to custom properties used via the deprecated `@apply` feature. It was previously called only on first render, and it is now up to the user to decide when this should be called.
- [Breaking] `render()` no longer clears the container it's rendered to. It now appends to the container by default.
- [Breaking] Expressions in comments are not rendered or updated.
- [Breaking] Template caching happens per callsite, not per template-tag/callsize pair. This means some rare forms of highly dynamic template tags are no longer supported.
- [Breaking] Arrays and other iterables passed to attribute bindings are not specially handled. Arrays will be rendered with their default toString representation. This means that `` html`<div class=${['a', 'b']}> `` will render `<div class="a,b">` instead of `<div class="a b">`. To get the old behavior, use `array.join(' ')`.
- Multiple bindings in a single attribute value don't require the attribute value is quoted, as long as there is no whitespace or other attribute-ending character in the attribute value. `` html`<div id=${a}-${b}> ``
- [Breaking] The directive and part APIs are significantly different. See the [README](README.md) for mroe details.
### Added
* Added `renderBefore` to render options. If specified, content is rendered before the node given via render options, e.g. `{renderBefore: node}`.
- Added `renderBefore` to render options. If specified, content is rendered before the node given via render options, e.g. `{renderBefore: node}`.
- Added development mode, which can be enabled by setting the `development` Node exports condition. See `README.md` for more details.
### Fixed
* All usage of `instanceof` has been removed, making rendering more likely to
work when multiple instances of the library interact.
* Template processing is more robust to expressions in places other than text and attribute values.
- All usage of `instanceof` has been removed, making rendering more likely to
work when multiple instances of the library interact.
- Template processing is more robust to expressions in places other than text and attribute values.
### Removed
* [Breaking] The `templateFactory` option of `RenderOptions` has been removed.
* [Breaking] TemplateProcessor has been removed.
* [Breaking] Symbols are not converted to a string before mutating DOM, so passing a Symbol to an attribute or text binding will result in an exception.
* [Breaking] The `until`, `asyncAppend` and `asyncReplace` directives are not implemented.
- [Breaking] The `templateFactory` option of `RenderOptions` has been removed.
- [Breaking] TemplateProcessor has been removed.
- [Breaking] Symbols are not converted to a string before mutating DOM, so passing a Symbol to an attribute or text binding will result in an exception.
- [Breaking] The `asyncAppend` and `asyncReplace` directives are not implemented.
## [1.3.0] - 2020-08-19
### Changed
* Set the "type" field in package.json to "module. ([#1146](https://github.com/Polymer/lit-html/pull/1146))
- Set the "type" field in package.json to "module. ([#1146](https://github.com/Polymer/lit-html/pull/1146))
### Added
* Added support for [Trusted Types](https://github.com/WICG/trusted-types). This support uses a policy named 'lit-html' for parsing the static parts of html literals, and ensures that we pass trusted type values through to the DOM when used in bindings. ([#1153](https://github.com/Polymer/lit-html/pull/1153))
* Export the `shadyTemplateFactory` from `lib/shady-render.js` ([#1135](https://github.com/Polymer/lit-html/pull/1135))
- Added support for [Trusted Types](https://github.com/WICG/trusted-types). This support uses a policy named 'lit-html' for parsing the static parts of html literals, and ensures that we pass trusted type values through to the DOM when used in bindings. ([#1153](https://github.com/Polymer/lit-html/pull/1153))
- Export the `shadyTemplateFactory` from `lib/shady-render.js` ([#1135](https://github.com/Polymer/lit-html/pull/1135))
## [1.2.1] - 2020-03-19
### Fixed
* Added TypeScript type declarations for older versions of TypeScript. We're currently testing back to TS 3.4. We can't commit to never breaking TypeScript builds, but we'll be supporting older versions as best we can.
- Added TypeScript type declarations for older versions of TypeScript. We're currently testing back to TS 3.4. We can't commit to never breaking TypeScript builds, but we'll be supporting older versions as best we can.
## [1.2.0] - 2020-03-18
### Added
* Added `unsafeSVG` directive to bind SVG source inside SVGs. ([#304](https://github.com/Polymer/lit-html/issues/304))
* Added `templateContent()` directive for stamping out the contents of an HTML template into a text binding. ([#1058](https://github.com/Polymer/lit-html/issues/1058))
* Added the `live()` directive. ([#877](https://github.com/Polymer/lit-html/issues/877))
- Added `unsafeSVG` directive to bind SVG source inside SVGs. ([#304](https://github.com/Polymer/lit-html/issues/304))
- Added `templateContent()` directive for stamping out the contents of an HTML template into a text binding. ([#1058](https://github.com/Polymer/lit-html/issues/1058))
- Added the `live()` directive. ([#877](https://github.com/Polymer/lit-html/issues/877))
### Fixed
* Fixed a bug where `classMap` and `styleMap` directives wouldn't render mutated objects. ([#972](https://github.com/Polymer/lit-html/issues/972))
* Fixed a bug where ifDefined() would set an attribute even when the value didn't change. ([#890](https://github.com/Polymer/lit-html/issues/890))
* Change `classMap` directive to set classes correctly on SVGs ([#1070](https://github.com/Polymer/lit-html/issues/1070)).
- Fixed a bug where `classMap` and `styleMap` directives wouldn't render mutated objects. ([#972](https://github.com/Polymer/lit-html/issues/972))
- Fixed a bug where ifDefined() would set an attribute even when the value didn't change. ([#890](https://github.com/Polymer/lit-html/issues/890))
- Change `classMap` directive to set classes correctly on SVGs ([#1070](https://github.com/Polymer/lit-html/issues/1070)).
## [1.1.2] - 2019-08-12
### Fixed
* Fixed a bug where bindings in comments could be written as text in some cases. ([#926](https://github.com/Polymer/lit-html/issues/926))
- Fixed a bug where bindings in comments could be written as text in some cases. ([#926](https://github.com/Polymer/lit-html/issues/926))
## [1.1.1] - 2019-07-09
### Changed
* `render` and `shady-render` now both accept any value that is renderable by `NodePart`. ([#910](https://github.com/Polymer/lit-html/issues/910))
- `render` and `shady-render` now both accept any value that is renderable by `NodePart`. ([#910](https://github.com/Polymer/lit-html/issues/910))
## [1.1.0] - 2019-05-20
### Changed
* Many small performance enhancements.
* Private names are now named with a `__` prefix ([#859](https://github.com/Polymer/lit-html/issues/859)).
- Many small performance enhancements.
- Private names are now named with a `__` prefix ([#859](https://github.com/Polymer/lit-html/issues/859)).
### Added
* Setup continuous benchmarking with Tachometer ([#887](https://github.com/Polymer/lit-html/issues/887)).
- Setup continuous benchmarking with Tachometer ([#887](https://github.com/Polymer/lit-html/issues/887)).
### Fixed
* Prevent empty styles from causing exceptions or breaking rendering when using `shady-render` ([#760](https://github.com/Polymer/lit-html/issues/760)).
* Primitive values in attributes are now always simply stringified, regardless of whether they are iterable. ([#830](https://github.com/Polymer/lit-html/pull/830))
* Adopt and upgrade template fragments after processing for parts ([#831](https://github.com/Polymer/lit-html/issues/831)).
* Fixed bindings with attribute-like expressions preceeding them ([#855](https://github.com/Polymer/lit-html/issues/855)).
* Fixed errors with bindings in HTML comments ([#882](https://github.com/Polymer/lit-html/issues/882)).
- Prevent empty styles from causing exceptions or breaking rendering when using `shady-render` ([#760](https://github.com/Polymer/lit-html/issues/760)).
- Primitive values in attributes are now always simply stringified, regardless of whether they are iterable. ([#830](https://github.com/Polymer/lit-html/pull/830))
- Adopt and upgrade template fragments after processing for parts ([#831](https://github.com/Polymer/lit-html/issues/831)).
- Fixed bindings with attribute-like expressions preceeding them ([#855](https://github.com/Polymer/lit-html/issues/855)).
- Fixed errors with bindings in HTML comments ([#882](https://github.com/Polymer/lit-html/issues/882)).
## [1.0.0] - 2019-02-05
### Changed
* Tons of docs updates ([#746](https://github.com/Polymer/lit-html/pull/746)), ([#675](https://github.com/Polymer/lit-html/pull/675)), ([#724](https://github.com/Polymer/lit-html/pull/724)), ([#753](https://github.com/Polymer/lit-html/pull/753)), ([#764](https://github.com/Polymer/lit-html/pull/764)), ([#763](https://github.com/Polymer/lit-html/pull/763)), ([#765](https://github.com/Polymer/lit-html/pull/765)), ([#767](https://github.com/Polymer/lit-html/pull/767)), ([#768](https://github.com/Polymer/lit-html/pull/768)), ([#734](https://github.com/Polymer/lit-html/pull/734)), ([#771](https://github.com/Polymer/lit-html/pull/771)), ([#766](https://github.com/Polymer/lit-html/pull/766)), ([#773](https://github.com/Polymer/lit-html/pull/773)), ([#770](https://github.com/Polymer/lit-html/pull/770)), ([#769](https://github.com/Polymer/lit-html/pull/769)), ([#777](https://github.com/Polymer/lit-html/pull/777)), ([#776](https://github.com/Polymer/lit-html/pull/776)), ([#754](https://github.com/Polymer/lit-html/pull/754)), ([#779](https://github.com/Polymer/lit-html/pull/779))
- Tons of docs updates ([#746](https://github.com/Polymer/lit-html/pull/746)), ([#675](https://github.com/Polymer/lit-html/pull/675)), ([#724](https://github.com/Polymer/lit-html/pull/724)), ([#753](https://github.com/Polymer/lit-html/pull/753)), ([#764](https://github.com/Polymer/lit-html/pull/764)), ([#763](https://github.com/Polymer/lit-html/pull/763)), ([#765](https://github.com/Polymer/lit-html/pull/765)), ([#767](https://github.com/Polymer/lit-html/pull/767)), ([#768](https://github.com/Polymer/lit-html/pull/768)), ([#734](https://github.com/Polymer/lit-html/pull/734)), ([#771](https://github.com/Polymer/lit-html/pull/771)), ([#766](https://github.com/Polymer/lit-html/pull/766)), ([#773](https://github.com/Polymer/lit-html/pull/773)), ([#770](https://github.com/Polymer/lit-html/pull/770)), ([#769](https://github.com/Polymer/lit-html/pull/769)), ([#777](https://github.com/Polymer/lit-html/pull/777)), ([#776](https://github.com/Polymer/lit-html/pull/776)), ([#754](https://github.com/Polymer/lit-html/pull/754)), ([#779](https://github.com/Polymer/lit-html/pull/779))
### Added
* Global version of `lit-html` on window ([#790](https://github.com/Polymer/lit-html/pull/790)).
- Global version of `lit-html` on window ([#790](https://github.com/Polymer/lit-html/pull/790)).
### Fixed
* Removed use of `any` outside of test code ([#741](https://github.com/Polymer/lit-html/pull/741)).
- Removed use of `any` outside of test code ([#741](https://github.com/Polymer/lit-html/pull/741)).
## [1.0.0-rc.2] - 2019-01-09
### Changed
* Performance improvements to template processing. ([#690](https://github.com/Polymer/lit-html/pull/690))
- Performance improvements to template processing. ([#690](https://github.com/Polymer/lit-html/pull/690))
### Added
* Added the `nothing` sentinel value which can be used to clear a part. ([#673](https://github.com/Polymer/lit-html/pull/673))
- Added the `nothing` sentinel value which can be used to clear a part. ([#673](https://github.com/Polymer/lit-html/pull/673))
### Fixed
* Fixed #702: a bug with the `unsafeHTML` directive when changing between unsafe and other values. ([#703](https://github.com/Polymer/lit-html/pull/703))
* Fixed #708: a bug with the `until` directive where placeholders could overwrite resolved Promises. ([#721](https://github.com/Polymer/lit-html/pull/721))
- Fixed #702: a bug with the `unsafeHTML` directive when changing between unsafe and other values. ([#703](https://github.com/Polymer/lit-html/pull/703))
- Fixed #708: a bug with the `until` directive where placeholders could overwrite resolved Promises. ([#721](https://github.com/Polymer/lit-html/pull/721))
## [1.0.0-rc.1] - 2018-12-13
### Fixed
* Documentation updates.
* Fixed typing for template_polyfill `createElement` call.
- Documentation updates.
- Fixed typing for template_polyfill `createElement` call.
## [0.14.0] - 2018-11-30
### Changed
* `until()` can now take any number of sync or async arguments. ([#555](https://github.com/Polymer/lit-html/pull/555))
* [Breaking] `guard()` supports multiple dependencies. If the first argument to `guard()` is an array, the array items are checked for equality to previous values. ([#666](https://github.com/Polymer/lit-html/pull/666))
* [Breaking] Renamed `classMap.js` and `styleMap.js` files to kebab-case. ([#644](https://github.com/Polymer/lit-html/pull/644))
- `until()` can now take any number of sync or async arguments. ([#555](https://github.com/Polymer/lit-html/pull/555))
- [Breaking] `guard()` supports multiple dependencies. If the first argument to `guard()` is an array, the array items are checked for equality to previous values. ([#666](https://github.com/Polymer/lit-html/pull/666))
- [Breaking] Renamed `classMap.js` and `styleMap.js` files to kebab-case. ([#644](https://github.com/Polymer/lit-html/pull/644))
### Added
* Added `cache()` directive. ([#646](https://github.com/Polymer/lit-html/pull/646))
* Removed Promise as a supposed node-position value type. ([#555](https://github.com/Polymer/lit-html/pull/555))
* Added a minimal `<template>` polyfill.
- Added `cache()` directive. ([#646](https://github.com/Polymer/lit-html/pull/646))
- Removed Promise as a supposed node-position value type. ([#555](https://github.com/Polymer/lit-html/pull/555))
- Added a minimal `<template>` polyfill.
### Removed
* [Breaking] Removed the `when()` directive. Users may achieve similar behavior by wrapping a ternary with the `cache()` directive.
- [Breaking] Removed the `when()` directive. Users may achieve similar behavior by wrapping a ternary with the `cache()` directive.
### Fixed
* Bound attribute names are rewritten to avoid IE/Edge removing SVG and style attributes. ([#640](https://github.com/Polymer/lit-html/pull/640))
* Ensure shady-render prepares styling for a scope before attaching child elements. ([#664](https://github.com/Polymer/lit-html/pull/664))
* Handle CSS Custom Variables in the styleMap directive. [#642](https://github.com/Polymer/lit-html/pull/642))
- Bound attribute names are rewritten to avoid IE/Edge removing SVG and style attributes. ([#640](https://github.com/Polymer/lit-html/pull/640))
- Ensure shady-render prepares styling for a scope before attaching child elements. ([#664](https://github.com/Polymer/lit-html/pull/664))
- Handle CSS Custom Variables in the styleMap directive. [#642](https://github.com/Polymer/lit-html/pull/642))
## [0.13.0] - 2018-11-08
### Changed
* [Breaking] Directives are now defined by passing the entire directive factory function to `directive()`. ([#562](https://github.com/Polymer/lit-html/pull/562))
= [Breaking] The `eventContext` render option has been changed to `host`.
- [Breaking] Directives are now defined by passing the entire directive factory function to `directive()`. ([#562](https://github.com/Polymer/lit-html/pull/562))
### Fixed
* Fix issue on obscure browsers that do not accept event listener objects by using callback as event part listener ([#581](https://github.com/Polymer/lit-html/pull/581))
* Fix KeyFn and ItemTemplate types ([#570](https://github.com/Polymer/lit-html/pull/570))
* Don't use export * to workaround rollup bug ([#556](https://github.com/Polymer/lit-html/pull/556))
* `eventContext` is no longer used as the `this` value for event listener objects (object with a `handleEvent` method), as the object itself is supposed to be the `this` value. ([#576](https://github.com/Polymer/lit-html/pull/576))
- Fix issue on obscure browsers that do not accept event listener objects by using callback as event part listener ([#581](https://github.com/Polymer/lit-html/pull/581))
- Fix KeyFn and ItemTemplate types ([#570](https://github.com/Polymer/lit-html/pull/570))
- Don't use export \* to workaround rollup bug ([#556](https://github.com/Polymer/lit-html/pull/556))
- `eventContext` is no longer used as the `this` value for event listener objects (object with a `handleEvent` method), as the object itself is supposed to be the `this` value. ([#576](https://github.com/Polymer/lit-html/pull/576))
## [0.12.0] - 2018-10-05
### Changed
* Re-implemented repeat directive for better performance ([#501](https://github.com/Polymer/lit-html/pull/501))
* Updated TypeScript dependency to 3.1
* [Breaking] `render()` now takes an options object as the third argument. ([#523](https://github.com/Polymer/lit-html/pull/523))
- Re-implemented repeat directive for better performance ([#501](https://github.com/Polymer/lit-html/pull/501))
- Updated TypeScript dependency to 3.1
- [Breaking] `render()` now takes an options object as the third argument. ([#523](https://github.com/Polymer/lit-html/pull/523))
### Added
* Event listeners are called with a configurable `this` reference, which is set via the `eventContext` option to `render()`. ([#523](https://github.com/Polymer/lit-html/pull/523))
* Support for event listener options, by passing the listener itself as both the second and third arguments to add/removeEventListener().
- Event listeners are called with a configurable `this` reference, which is set via the `eventContext` option to `render()`. ([#523](https://github.com/Polymer/lit-html/pull/523))
- Support for event listener options, by passing the listener itself as both the second and third arguments to add/removeEventListener().
## [0.11.4] - 2018-09-17
### Fixed
* Fixed issues with `shady-render` introduced in 0.11.3 ([#504](https://github.com/Polymer/lit-html/issues/504) and [#505](https://github.com/Polymer/lit-html/issues/505)).
- Fixed issues with `shady-render` introduced in 0.11.3 ([#504](https://github.com/Polymer/lit-html/issues/504) and [#505](https://github.com/Polymer/lit-html/issues/505)).
## [0.11.3] - 2018-09-13
### Changed
* Moved upgrading of custom elements in template fragments to a common location in TemplateInstance ([#489](https://github.com/Polymer/lit-html/pull/489))
* Rewrite render() to reuse the logic in NodePart. render() now supports all the data types that NodeParts do. ([#491](https://github.com/Polymer/lit-html/pull/491))
- Moved upgrading of custom elements in template fragments to a common location in TemplateInstance ([#489](https://github.com/Polymer/lit-html/pull/489))
- Rewrite render() to reuse the logic in NodePart. render() now supports all the data types that NodeParts do. ([#491](https://github.com/Polymer/lit-html/pull/491))
### Fixed
* Fixed bug when using the ShadyCSS @apply` shim. ([#502](https://github.com/Polymer/lit-html/pull/502))
- Fixed bug when using the ShadyCSS @apply` shim. ([#502](https://github.com/Polymer/lit-html/pull/502))
## [0.11.2] - 2018-09-12
### Added
* Added `classMap` and `styleMap` directives ([#486](https://github.com/Polymer/lit-html/pull/486))
- Added `classMap` and `styleMap` directives ([#486](https://github.com/Polymer/lit-html/pull/486))
### Fixed
* Fixed bug in asyncReplace when rerendering the same iterable ([#485](https://github.com/Polymer/lit-html/pull/485))
* Update properties before upgrading custom elements ([#455](https://github.com/Polymer/lit-html/pull/455))
* Cache the ShadyCSS version lookup ([#477](https://github.com/Polymer/lit-html/pull/477))
- Fixed bug in asyncReplace when rerendering the same iterable ([#485](https://github.com/Polymer/lit-html/pull/485))
- Update properties before upgrading custom elements ([#455](https://github.com/Polymer/lit-html/pull/455))
- Cache the ShadyCSS version lookup ([#477](https://github.com/Polymer/lit-html/pull/477))

@@ -181,93 +306,98 @@ ## [0.11.1] - 2018-09-02

### Changed
* Eliminated a cycle in the module import graph ([#472](https://github.com/Polymer/lit-html/pull/472))
* Remove the default value for the templateProcessor parameter in TemplateResult#constuctor, making it a required paremeter ([#472](https://github.com/Polymer/lit-html/pull/472))
- Eliminated a cycle in the module import graph ([#472](https://github.com/Polymer/lit-html/pull/472))
- Remove the default value for the templateProcessor parameter in TemplateResult#constuctor, making it a required paremeter ([#472](https://github.com/Polymer/lit-html/pull/472))
## [0.11.0] - 2018-08-28
### Added
* Added support for property, event, and boolean bindings to default syntax ([#398](https://github.com/Polymer/lit-html/pull/398))
* Added guard directive ([#438](https://github.com/Polymer/lit-html/pull/438))
* Added when directive ([#439](https://github.com/Polymer/lit-html/pull/439))
- Added support for property, event, and boolean bindings to default syntax ([#398](https://github.com/Polymer/lit-html/pull/398))
- Added guard directive ([#438](https://github.com/Polymer/lit-html/pull/438))
- Added when directive ([#439](https://github.com/Polymer/lit-html/pull/439))
### Changed
* Split implementation into multiple small modules and merged lit-html.js and core.js ([#436](https://github.com/Polymer/lit-html/pull/436))
* Moved directives into top-level `directives/` directory ([#436](https://github.com/Polymer/lit-html/pull/436))
* Replaced `PartCallback` with `TemplateProcessor` ([#405](https://github.com/Polymer/lit-html/pull/405))
* Unified `NodePart` and `AttributePart` interfaces ([#400](https://github.com/Polymer/lit-html/pull/400))
* AttributePart#setValue() takes a single value
* `Part` has separate `setValue()` and `commit()` phases
* Added `AttributeCommitter` to commit attribute values once for multiple `AttributeParts`
- Split implementation into multiple small modules and merged lit-html.js and core.js ([#436](https://github.com/Polymer/lit-html/pull/436))
- Moved directives into top-level `directives/` directory ([#436](https://github.com/Polymer/lit-html/pull/436))
- Replaced `PartCallback` with `TemplateProcessor` ([#405](https://github.com/Polymer/lit-html/pull/405))
- Unified `NodePart` and `AttributePart` interfaces ([#400](https://github.com/Polymer/lit-html/pull/400))
- AttributePart#setValue() takes a single value
- `Part` has separate `setValue()` and `commit()` phases
- Added `AttributeCommitter` to commit attribute values once for multiple `AttributeParts`
### Removed
* Removed lit-extended.js ([#436](https://github.com/Polymer/lit-html/pull/436))
- Removed lit-extended.js ([#436](https://github.com/Polymer/lit-html/pull/436))
### Fixed
* Render initial undefined values in attributes ([#377](https://github.com/Polymer/lit-html/pull/377))
* Handle case-sensitive attributes like `viewBox` correctly ([#376](https://github.com/Polymer/lit-html/pull/376))
* Support bindings in `<template>` elements ([#343](https://github.com/Polymer/lit-html/pull/343))
* Don’t break templates when HTML comments have bindings in them ([#446](https://github.com/Polymer/lit-html/pull/446))
* IE: Don't use Set() constructor arguments ([#401](https://github.com/Polymer/lit-html/pull/401))
* Handle forms as Node instead of iterable ([#404](https://github.com/Polymer/lit-html/pull/404))
* Update values after upgrading custom elements ([#385](https://github.com/Polymer/lit-html/pull/385))
* Dirty check primitive values passed to unsafeHTML() ([#384](https://github.com/Polymer/lit-html/pull/384))
* Handle forms as Node instead of iterable ([#404](https://github.com/Polymer/lit-html/pull/404))
* Upgrade disconnected custom elements before setting properties on them. ([#442](https://github.com/Polymer/lit-html/pull/442))
* Fix style attribute bindings in IE ([#448](https://github.com/Polymer/lit-html/pull/448))
- Render initial undefined values in attributes ([#377](https://github.com/Polymer/lit-html/pull/377))
- Handle case-sensitive attributes like `viewBox` correctly ([#376](https://github.com/Polymer/lit-html/pull/376))
- Support bindings in `<template>` elements ([#343](https://github.com/Polymer/lit-html/pull/343))
- Don’t break templates when HTML comments have bindings in them ([#446](https://github.com/Polymer/lit-html/pull/446))
- IE: Don't use Set() constructor arguments ([#401](https://github.com/Polymer/lit-html/pull/401))
- Handle forms as Node instead of iterable ([#404](https://github.com/Polymer/lit-html/pull/404))
- Update values after upgrading custom elements ([#385](https://github.com/Polymer/lit-html/pull/385))
- Dirty check primitive values passed to unsafeHTML() ([#384](https://github.com/Polymer/lit-html/pull/384))
- Handle forms as Node instead of iterable ([#404](https://github.com/Polymer/lit-html/pull/404))
- Upgrade disconnected custom elements before setting properties on them. ([#442](https://github.com/Polymer/lit-html/pull/442))
- Fix style attribute bindings in IE ([#448](https://github.com/Polymer/lit-html/pull/448))
## [0.10.1] - 2018-06-13
* Added `noChange` - Value in favour of `directiveValue` (deprecated).
* A `noChange` - Value signals that a value was handled by a directive and should not be written to the DOM
* Updated shady-render to render styles in order, work with `@apply`, and work in browers where CSS Custom Properties must be polyfilled, like IE 11.
* Introduced API to modify template contents safely without breaking template parts
* `insertNodeIntoTemplate(template: Template, node: Node, refNode: Node|null)`
* `removeNodesFromTemplate(template: Template, nodesToRemove: Set<Node>)`
- Added `noChange` - Value in favour of `directiveValue` (deprecated).
- A `noChange` - Value signals that a value was handled by a directive and should not be written to the DOM
- Updated shady-render to render styles in order, work with `@apply`, and work in browers where CSS Custom Properties must be polyfilled, like IE 11.
- Introduced API to modify template contents safely without breaking template parts
- `insertNodeIntoTemplate(template: Template, node: Node, refNode: Node|null)`
- `removeNodesFromTemplate(template: Template, nodesToRemove: Set<Node>)`
## [0.10.0] - 2018-05-03
* Added IE11 support
* Declarative events in lit-extended are more efficient when handlers change
- Added IE11 support
- Declarative events in lit-extended are more efficient when handlers change
## [0.9.0] - 2018-02-01
* Refactored how template tags and `render()` are implemented so that all
- Refactored how template tags and `render()` are implemented so that all
specialization of template syntax is done in tags, not `render()`, allowing
for the mixining of templates of different syntaxes, and for hooks in
`render()` to change templates before they're initially processed.
* Added ShadyCSS support in lib/shady-render.js. It's exported render function
- Added ShadyCSS support in lib/shady-render.js. It's exported render function
will pass templates to ShadyCSS's `prepareTemplate()` function to process style
tags and elements in the template for emulate CSS scoping.
* lit-extended: Attribute bindings with a `?` suffix on the name now act as boolean
- lit-extended: Attribute bindings with a `?` suffix on the name now act as boolean
attributes. The attribute will be removed for falsey values and set to `''` for
truthy values, matching the HTML specification behavior for boolean attributes.
* Fixed a bug where directives rendered incorrectly on AttributeParts and PropertyParts
- Fixed a bug where directives rendered incorrectly on AttributeParts and PropertyParts
## [0.8.0] - 2018-01-12
* Allow all valid HTML attribute names, including emoji and Angular-style
- Allow all valid HTML attribute names, including emoji and Angular-style
`(foo)=` and `[foo]=` names.
* Drastically improved performance of the `repeat` directive.
* Fixed an issue with expressions directly following elements.
* Fixed numerous bugs with the `repeat` directive.
* Performance improvements for template setup
* Internal code cleanup
* Support synchronous thenables
* Added the `asyncAppend` and `asyncReplace` directives to handle async iterable values in expressions.
- Drastically improved performance of the `repeat` directive.
- Fixed an issue with expressions directly following elements.
- Fixed numerous bugs with the `repeat` directive.
- Performance improvements for template setup
- Internal code cleanup
- Support synchronous thenables
- Added the `asyncAppend` and `asyncReplace` directives to handle async iterable values in expressions.
## [0.7.0] - 2017-10-06
* Added the `svg` template tag for creating partial SVG content
* Support for expressions inside tables and other elements with limited permitted content
* Only remove whitespace between elements, or at the start or end of elements
* Fixed bugs with rendering iterables
* A few IE/Edge fixes. Closer to full support.
- Added the `svg` template tag for creating partial SVG content
- Support for expressions inside tables and other elements with limited permitted content
- Only remove whitespace between elements, or at the start or end of elements
- Fixed bugs with rendering iterables
- A few IE/Edge fixes. Closer to full support.
## [0.6.0] - 2017-09-01
* Fixed removing event handlers when setting them to `undefined`.
* Allow the text "{{}}" to appear in templates.
* Optimized clearing of Parts.
* Added `unsafeHTML()` directive to bind values as HTML source.
* Optimizations, simplification and bug fixes of Array handling code.
* Update to extension API: added partCallback parameter to `render()`.
* Added the `directive()` decorator function to create directives. Functions values are no longer treated as directive by default, simplifying declarative event handlers.
- Fixed removing event handlers when setting them to `undefined`.
- Allow the text "{{}}" to appear in templates.
- Optimized clearing of Parts.
- Added `unsafeHTML()` directive to bind values as HTML source.
- Optimizations, simplification and bug fixes of Array handling code.
- Update to extension API: added partCallback parameter to `render()`.
- Added the `directive()` decorator function to create directives. Functions values are no longer treated as directive by default, simplifying declarative event handlers.

@@ -14,4 +14,4 @@ /**

*/
import { TemplateResult, NodePart } from '../lit-html.js';
import { NodePartState } from '../parts.js';
import { TemplateResult, ChildPart } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
/**

@@ -31,13 +31,139 @@ * Enables fast switching between multiple templates by caching the DOM nodes

*/
export declare const cache: (v: unknown) => {
_$litDirective$: {
new (): {
templateCache: WeakMap<TemplateStringsArray, NodePartState>;
value?: TemplateResult | undefined;
render(v: unknown): unknown;
update(part: NodePart, [v]: [v: unknown]): unknown;
export declare const cache: (v: unknown) => import("../directive.js").DirectiveResult<{
new (partInfo: PartInfo): {
templateCache: WeakMap<TemplateStringsArray, {
readonly type: 2;
readonly options: import("../lit-html.js").RenderOptions | undefined;
_$committedValue: unknown;
__directive?: Directive | undefined;
_$startNode: ChildNode;
_$endNode: ChildNode | null;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setChildPartConnected?: ((isConnected: boolean, removeFromParent?: boolean | undefined, from?: number | undefined) => void) | undefined;
setConnected: (isConnected: boolean) => void;
readonly parentNode: Node;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent) => void;
_$getTemplate: (strings: TemplateStringsArray, result: TemplateResult) => {
_$element: HTMLTemplateElement;
_parts: ({
readonly _type: 2;
readonly _index: number;
} | {
readonly _type: 1;
readonly _index: number;
readonly _name: string;
readonly _constructor: new (element: HTMLElement, name: string, strings: readonly string[], parent: import("../lit-html.js").Disconnectable | undefined, options: import("../lit-html.js").RenderOptions | undefined) => {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
};
readonly _strings: readonly string[];
} | {
readonly _type: 6;
readonly _index: number;
} | {
readonly _type: 7;
readonly _index: number;
})[];
_$options?: import("../lit-html.js").RenderOptions | undefined;
_$createElement: (html: string) => HTMLTemplateElement;
};
_$clear: (start?: ChildNode | null, from?: number | undefined) => void;
}>;
value?: TemplateResult | undefined;
render(v: unknown): unknown[];
update(containerPart: ChildPart, [v]: [v: unknown]): unknown[];
__part: {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
} | {
readonly type: 2;
readonly options: import("../lit-html.js").RenderOptions | undefined;
_$committedValue: unknown;
__directive?: Directive | undefined;
_$startNode: ChildNode;
_$endNode: ChildNode | null;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setChildPartConnected?: ((isConnected: boolean, removeFromParent?: boolean | undefined, from?: number | undefined) => void) | undefined;
setConnected: (isConnected: boolean) => void;
readonly parentNode: Node;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent) => void;
_$getTemplate: (strings: TemplateStringsArray, result: TemplateResult) => {
_$element: HTMLTemplateElement;
_parts: ({
readonly _type: 2;
readonly _index: number;
} | {
readonly _type: 1;
readonly _index: number;
readonly _name: string;
readonly _constructor: new (element: HTMLElement, name: string, strings: readonly string[], parent: import("../lit-html.js").Disconnectable | undefined, options: import("../lit-html.js").RenderOptions | undefined) => {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
};
readonly _strings: readonly string[];
} | {
readonly _type: 6;
readonly _index: number;
} | {
readonly _type: 7;
readonly _index: number;
})[];
_$options?: import("../lit-html.js").RenderOptions | undefined;
_$createElement: (html: string) => HTMLTemplateElement;
};
_$clear: (start?: ChildNode | null, from?: number | undefined) => void;
} | {
readonly type: 6;
__directive?: Directive | undefined;
_$committedValue: undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
options: import("../lit-html.js").RenderOptions | undefined;
element: Element;
_$setValue: (value: unknown) => void;
};
__attributeIndex: number | undefined;
__directive?: Directive | undefined;
_$parent: import("../lit-html.js").Disconnectable;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setDirectiveConnected?(isConnected: boolean): void;
_resolve(props: unknown[]): unknown;
};
values: [v: unknown];
};
}>;
//# sourceMappingURL=cache.d.ts.map

@@ -14,4 +14,5 @@ /**

*/
import { directive, Directive } from '../lit-html.js';
import { detachNodePart, restoreNodePart } from '../parts.js';
import { render, nothing } from '../lit-html.js';
import { directive, Directive, } from '../directive.js';
import { clearPart, getComittedValue, insertPart, isTemplateResult, setComittedValue, } from '../directive-helpers.js';
/**

@@ -32,21 +33,44 @@ * Enables fast switching between multiple templates by caching the DOM nodes

export const cache = directive(class extends Directive {
constructor() {
super(...arguments);
constructor(partInfo) {
super(partInfo);
this.templateCache = new WeakMap();
}
render(v) {
return v;
// Return an array of the value to induce lit-html to create a ChildPart
// for the value that we can move into the cache.
return [v];
}
update(part, [v]) {
// If the new value is not a TemplateResult from the same Template as the
// previous value, move the nodes from the DOM into the cache.
if (this.value !== undefined &&
this.value.strings !== v.strings) {
this.templateCache.set(this.value.strings, detachNodePart(part));
update(containerPart, [v]) {
// If the previous value is a TemplateResult and the new value is not,
// or is a different Template as the previous value, move the child part
// into the cache.
if (isTemplateResult(this.value) &&
(!isTemplateResult(v) || this.value.strings !== v.strings)) {
// This is always an array because we return [v] in render()
const partValue = getComittedValue(containerPart);
const childPart = partValue.pop();
let cachedContainerPart = this.templateCache.get(this.value.strings);
if (cachedContainerPart === undefined) {
const fragment = document.createDocumentFragment();
cachedContainerPart = render(nothing, fragment);
this.templateCache.set(this.value.strings, cachedContainerPart);
}
// Move into cache
setComittedValue(cachedContainerPart, [childPart]);
insertPart(cachedContainerPart, undefined, childPart);
}
// If the new value is a TemplateResult, try to restore it from cache
if (v._$litType$ !== undefined) {
let cachedTemplate = this.templateCache.get(v.strings);
if (cachedTemplate !== undefined) {
restoreNodePart(part, cachedTemplate);
// If the new value is a TemplateResult and the previous value is not,
// or is a different Template as the previous value, restore the child
// part from the cache.
if (isTemplateResult(v) &&
(!isTemplateResult(this.value) || this.value.strings !== v.strings)) {
const cachedContainerPart = this.templateCache.get(v.strings);
if (cachedContainerPart !== undefined) {
// Move the cached part back into the container part value
const partValue = getComittedValue(cachedContainerPart);
const cachedPart = partValue.pop();
// Move cached part back into DOM
clearPart(containerPart);
insertPart(containerPart, undefined, cachedPart);
setComittedValue(containerPart, [cachedPart]);
}

@@ -53,0 +77,0 @@ this.value = v;

@@ -14,3 +14,4 @@ /**

*/
import { AttributePart, Directive, PartInfo } from '../lit-html.js';
import { AttributePart, noChange } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
/**

@@ -28,5 +29,5 @@ * A key-value set of class names to truthy values.

previousClasses?: Set<string>;
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(classInfo: ClassInfo): string;
update(part: AttributePart, [classInfo]: [ClassInfo]): {};
update(part: AttributePart, [classInfo]: DirectiveParameters<this>): string | typeof noChange;
}

@@ -45,9 +46,6 @@ /**

*
* @param classInfo {ClassInfo}
* @param classInfo
*/
export declare const classMap: (classInfo: ClassInfo) => {
_$litDirective$: typeof ClassMap;
values: [classInfo: ClassInfo];
};
export declare const classMap: (classInfo: ClassInfo) => import("../directive.js").DirectiveResult<typeof ClassMap>;
export {};
//# sourceMappingURL=class-map.d.ts.map

@@ -14,9 +14,10 @@ /**

*/
import { directive, Directive, noChange, ATTRIBUTE_PART, } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { directive, Directive, PartType, } from '../directive.js';
class ClassMap extends Directive {
constructor(part) {
super();
if (part.type !== ATTRIBUTE_PART ||
part.name !== 'class' ||
(part.strings !== undefined && part.strings.length > 2)) {
constructor(partInfo) {
super(partInfo);
if (partInfo.type !== PartType.ATTRIBUTE ||
partInfo.name !== 'class' ||
partInfo.strings?.length > 2) {
throw new Error('The `classMap` directive must be used in the `class` attribute ' +

@@ -83,5 +84,5 @@ 'and must be the only part in the attribute.');

*
* @param classInfo {ClassInfo}
* @param classInfo
*/
export const classMap = directive(ClassMap);
//# sourceMappingURL=class-map.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, Part } from '../lit-html.js';
import { Part } from '../lit-html.js';
import { Directive, DirectiveParameters } from '../directive.js';
declare class Guard extends Directive {
previousValue: unknown;
render(_value: unknown, f: () => unknown): unknown;
update(_part: Part, [value, f]: Parameters<this['render']>): unknown;
update(_part: Part, [value, f]: DirectiveParameters<this>): unknown;
}

@@ -60,7 +61,4 @@ /**

*/
export declare const guard: (_value: unknown, f: () => unknown) => {
_$litDirective$: typeof Guard;
values: [_value: unknown, f: () => unknown];
};
export declare const guard: (_value: unknown, f: () => unknown) => import("../directive.js").DirectiveResult<typeof Guard>;
export {};
//# sourceMappingURL=guard.d.ts.map

@@ -14,3 +14,4 @@ /**

*/
import { directive, Directive, noChange } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { directive, Directive } from '../directive.js';
// A sentinal that indicates guard() hasn't rendered anything yet

@@ -17,0 +18,0 @@ const initialValue = {};

@@ -21,3 +21,3 @@ /**

*/
export const ifDefined = (value) => value !== null && value !== void 0 ? value : nothing;
export const ifDefined = (value) => value ?? nothing;
//# sourceMappingURL=if-defined.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, AttributePart, PartInfo } from '../lit-html.js';
import { AttributePart } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
declare class LiveDirective extends Directive {
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(value: unknown): unknown;
update(part: AttributePart, [value]: Parameters<this['render']>): unknown;
update(part: AttributePart, [value]: DirectiveParameters<this>): unknown;
}

@@ -32,4 +33,4 @@ /**

* bindings hasn't, lit-html won't know to update the DOM value and will leave
* it alone. If this is not what you want—if you want to overwrite the DOM
* value with the bound value no matter what—use the `live()` directive:
* it alone. If this is not what you want--if you want to overwrite the DOM
* value with the bound value no matter what--use the `live()` directive:
*

@@ -44,7 +45,4 @@ * html`<input .value=${live(x)}>`

*/
export declare const live: (value: unknown) => {
_$litDirective$: typeof LiveDirective;
values: [value: unknown];
};
export declare const live: (value: unknown) => import("../directive.js").DirectiveResult<typeof LiveDirective>;
export {};
//# sourceMappingURL=live.d.ts.map

@@ -14,13 +14,14 @@ /**

*/
import { directive, Directive, noChange, nothing, NODE_PART, EVENT_PART, PROPERTY_PART, BOOLEAN_ATTRIBUTE_PART, ATTRIBUTE_PART, } from '../lit-html.js';
// A sentinal value that can never appear as a part value except when set by
// live(). Used to force a dirty-check to fail and cause a re-render.
const RESET_VALUE = {};
import { noChange, nothing } from '../lit-html.js';
import { directive, Directive, PartType, } from '../directive.js';
import { isSingleExpression, setComittedValue } from '../directive-helpers.js';
class LiveDirective extends Directive {
constructor(part) {
super();
if (part.type === EVENT_PART || part.type === NODE_PART) {
throw new Error('The `live` directive is not allowed on text or event bindings');
constructor(partInfo) {
super(partInfo);
if (!(partInfo.type === PartType.PROPERTY ||
partInfo.type === PartType.ATTRIBUTE ||
partInfo.type === PartType.BOOLEAN_ATTRIBUTE)) {
throw new Error('The `live` directive is not allowed on child or event bindings');
}
if (part.strings !== undefined) {
if (!isSingleExpression(partInfo)) {
throw new Error('`live` bindings can only contain a single expression');

@@ -33,3 +34,3 @@ }

update(part, [value]) {
if (value === noChange) {
if (value === noChange || value === nothing) {
return value;

@@ -42,3 +43,3 @@ }

// interface?
if (part.type === PROPERTY_PART) {
if (part.type === PartType.PROPERTY) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any

@@ -49,8 +50,8 @@ if (value === element[name]) {

}
else if (part.type === BOOLEAN_ATTRIBUTE_PART) {
if ((value === nothing ? false : !!value) === element.hasAttribute(name)) {
else if (part.type === PartType.BOOLEAN_ATTRIBUTE) {
if (!!value === element.hasAttribute(name)) {
return noChange;
}
}
else if (part.type === ATTRIBUTE_PART) {
else if (part.type === PartType.ATTRIBUTE) {
if (element.getAttribute(name) === String(value)) {

@@ -60,5 +61,5 @@ return noChange;

}
// Setting the part's value to RESET_VALUE causes its dirty-check to fail
// so that it always sets the value.
part._value = RESET_VALUE;
// Resets the part's value, causing its dirty-check to fail so that it
// always sets the value.
setComittedValue(part);
return value;

@@ -78,4 +79,4 @@ }

* bindings hasn't, lit-html won't know to update the DOM value and will leave
* it alone. If this is not what you want—if you want to overwrite the DOM
* value with the bound value no matter what—use the `live()` directive:
* it alone. If this is not what you want--if you want to overwrite the DOM
* value with the bound value no matter what--use the `live()` directive:
*

@@ -82,0 +83,0 @@ * html`<input .value=${live(x)}>`

@@ -14,4 +14,5 @@ /**

*/
import { directive, NodePart, Directive, noChange, } from '../lit-html.js';
import { createAndInsertPart, getPartValue, insertPartBefore, removePart, setPartValue, } from '../parts.js';
import { noChange } from '../lit-html.js';
import { directive, Directive, PartType } from '../directive.js';
import { insertPart, getComittedValue, removePart, setComittedValue, setChildPartValue, } from '../directive-helpers.js';
// Helper for generating a map of array item to its index over a subset

@@ -47,5 +48,5 @@ // of an array (used to lazily generate `newKeyToIndexMap` and

class RepeatDirective extends Directive {
constructor(part) {
super();
if (!(part instanceof NodePart)) {
constructor(partInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error('repeat can only be used in text bindings');

@@ -79,6 +80,5 @@ }

update(containerPart, [items, keyFnOrTemplate, template]) {
var _a;
// Old part & key lists are retrieved from the last update
// TODO: deal with directive being swapped out?
let oldParts = getPartValue(containerPart);
const oldParts = getComittedValue(containerPart);
const { values: newValues, keys: newKeys } = this._getValuesAndKeys(items, keyFnOrTemplate, template);

@@ -89,3 +89,3 @@ if (!oldParts) {

}
const oldKeys = ((_a = this.itemKeys) !== null && _a !== void 0 ? _a : (this.itemKeys = []));
const oldKeys = (this.itemKeys ?? (this.itemKeys = []));
// New part list will be built up as we go (either reused from

@@ -314,3 +314,3 @@ // old parts or created for new keys in this update). This is

// Old head matches new head; update in place
newParts[newHead] = setPartValue(oldParts[oldHead], newValues[newHead]);
newParts[newHead] = setChildPartValue(oldParts[oldHead], newValues[newHead]);
oldHead++;

@@ -321,3 +321,3 @@ newHead++;

// Old tail matches new tail; update in place
newParts[newTail] = setPartValue(oldParts[oldTail], newValues[newTail]);
newParts[newTail] = setChildPartValue(oldParts[oldTail], newValues[newTail]);
oldTail--;

@@ -328,4 +328,4 @@ newTail--;

// Old head matches new tail; update and move to new tail
newParts[newTail] = setPartValue(oldParts[oldHead], newValues[newTail]);
insertPartBefore(containerPart, oldParts[oldHead], newParts[newTail + 1]);
newParts[newTail] = setChildPartValue(oldParts[oldHead], newValues[newTail]);
insertPart(containerPart, newParts[newTail + 1], oldParts[oldHead]);
oldHead++;

@@ -336,4 +336,4 @@ newTail--;

// Old tail matches new head; update and move to new head
newParts[newHead] = setPartValue(oldParts[oldTail], newValues[newHead]);
insertPartBefore(containerPart, oldParts[oldTail], oldParts[oldHead]);
newParts[newHead] = setChildPartValue(oldParts[oldTail], newValues[newHead]);
insertPart(containerPart, oldParts[oldHead], oldParts[oldTail]);
oldTail--;

@@ -368,4 +368,4 @@ newHead++;

// insert it
const newPart = createAndInsertPart(containerPart, oldParts[oldHead]);
setPartValue(newPart, newValues[newHead]);
const newPart = insertPart(containerPart, oldParts[oldHead]);
setChildPartValue(newPart, newValues[newHead]);
newParts[newHead] = newPart;

@@ -375,4 +375,4 @@ }

// Reuse old part
newParts[newHead] = setPartValue(oldPart, newValues[newHead]);
insertPartBefore(containerPart, oldPart, oldParts[oldHead]);
newParts[newHead] = setChildPartValue(oldPart, newValues[newHead]);
insertPart(containerPart, oldParts[oldHead], oldPart);
// This marks the old part as having been used, so that

@@ -390,4 +390,4 @@ // it will be skipped in the first two checks above

// tail, since old pointers are no longer valid
const newPart = createAndInsertPart(containerPart, newParts[newTail + 1]);
setPartValue(newPart, newValues[newHead]);
const newPart = insertPart(containerPart, newParts[newTail + 1]);
setChildPartValue(newPart, newValues[newHead]);
newParts[newHead++] = newPart;

@@ -405,4 +405,3 @@ }

// Directly set part value, bypassing it's dirty-checking
// TODO (justinfagnani): resolve with https://github.com/Polymer/lit-html/issues/1261
containerPart._value = newParts;
setComittedValue(containerPart, newParts);
return noChange;

@@ -409,0 +408,0 @@ }

@@ -14,3 +14,4 @@ /**

*/
import { AttributePart, Directive, PartInfo } from '../lit-html.js';
import { AttributePart, noChange } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
/**

@@ -28,5 +29,5 @@ * A key-value set of CSS properties and values.

previousStyleProperties?: Set<string>;
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(styleInfo: StyleInfo): string;
update(part: AttributePart, [styleInfo]: Parameters<this['render']>): {};
update(part: AttributePart, [styleInfo]: DirectiveParameters<this>): string | typeof noChange;
}

@@ -48,9 +49,6 @@ /**

*
* @param styleInfo {StyleInfo}
* @param styleInfo
*/
export declare const styleMap: (styleInfo: StyleInfo) => {
_$litDirective$: typeof StyleMap;
values: [styleInfo: StyleInfo];
};
export declare const styleMap: (styleInfo: StyleInfo) => import("../directive.js").DirectiveResult<typeof StyleMap>;
export {};
//# sourceMappingURL=style-map.d.ts.map

@@ -14,9 +14,10 @@ /**

*/
import { directive, Directive, noChange, ATTRIBUTE_PART, } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { directive, Directive, PartType, } from '../directive.js';
class StyleMap extends Directive {
constructor(part) {
super();
if (part.type !== ATTRIBUTE_PART ||
part.name !== 'style' ||
(part.strings !== undefined && part.strings.length > 2)) {
constructor(partInfo) {
super(partInfo);
if (partInfo.type !== PartType.ATTRIBUTE ||
partInfo.name !== 'style' ||
partInfo.strings?.length > 2) {
throw new Error('The `styleMap` directive must be used in the `style` attribute ' +

@@ -98,5 +99,5 @@ 'and must be the only part in the attribute.');

*
* @param styleInfo {StyleInfo}
* @param styleInfo
*/
export const styleMap = directive(StyleMap);
//# sourceMappingURL=style-map.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, PartInfo } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
declare class TemplateContent extends Directive {
private __previousTemplate?;
constructor(part: PartInfo);
render(template: HTMLTemplateElement): {};
private _previousTemplate?;
constructor(partInfo: PartInfo);
render(template: HTMLTemplateElement): DocumentFragment | typeof noChange;
}

@@ -28,7 +29,4 @@ /**

*/
export declare const templateContent: (template: HTMLTemplateElement) => {
_$litDirective$: typeof TemplateContent;
values: [template: HTMLTemplateElement];
};
export declare const templateContent: (template: HTMLTemplateElement) => import("../directive.js").DirectiveResult<typeof TemplateContent>;
export {};
//# sourceMappingURL=template-content.d.ts.map

@@ -14,15 +14,16 @@ /**

*/
import { Directive, directive, noChange, NODE_PART, } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { directive, Directive, PartType } from '../directive.js';
class TemplateContent extends Directive {
constructor(part) {
super();
if (part.type !== NODE_PART) {
throw new Error('templateContent can only be used in text bindings');
constructor(partInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error('templateContent can only be used in child bindings');
}
}
render(template) {
if (this.__previousTemplate === template) {
if (this._previousTemplate === template) {
return noChange;
}
this.__previousTemplate = template;
this._previousTemplate = template;
return document.importNode(template.content, true);

@@ -29,0 +30,0 @@ }

@@ -14,3 +14,4 @@ /**

*/
import { Directive, TemplateResult, PartInfo } from '../lit-html.js';
import { nothing, TemplateResult, noChange } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
export declare class UnsafeHTML extends Directive {

@@ -21,4 +22,4 @@ static directiveName: string;

templateResult?: TemplateResult;
constructor(part: PartInfo);
render(value: string): string | TemplateResult | undefined;
constructor(partInfo: PartInfo);
render(value: string | typeof nothing | typeof noChange): TemplateResult | typeof noChange | typeof nothing | undefined;
}

@@ -32,6 +33,3 @@ /**

*/
export declare const unsafeHTML: (value: string) => {
_$litDirective$: typeof UnsafeHTML;
values: [value: string];
};
export declare const unsafeHTML: (value: string | typeof noChange | typeof nothing) => import("../directive.js").DirectiveResult<typeof UnsafeHTML>;
//# sourceMappingURL=unsafe-html.d.ts.map

@@ -14,10 +14,11 @@ /**

*/
import { directive, Directive, nothing, noChange, NODE_PART, } from '../lit-html.js';
import { nothing, noChange } from '../lit-html.js';
import { directive, Directive, PartType } from '../directive.js';
const HTML_RESULT = 1;
export class UnsafeHTML extends Directive {
constructor(part) {
super();
constructor(partInfo) {
super(partInfo);
this.value = nothing;
if (part.type !== NODE_PART) {
throw new Error(`${this.constructor.directiveName}() can only be used in text bindings`);
if (partInfo.type !== PartType.CHILD) {
throw new Error(`${this.constructor.directiveName}() can only be used in child bindings`);
}

@@ -42,2 +43,3 @@ }

const strings = [value];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
strings.raw = strings;

@@ -44,0 +46,0 @@ // WARNING: impersonating a TemplateResult like this is extremely

@@ -26,7 +26,4 @@ /**

*/
export declare const unsafeSVG: (value: string) => {
_$litDirective$: typeof UnsafeSVG;
values: [value: string];
};
export declare const unsafeSVG: (value: string | typeof import("../lit-html.js").noChange | typeof import("../lit-html.js").nothing) => import("../directive.js").DirectiveResult<typeof UnsafeSVG>;
export {};
//# sourceMappingURL=unsafe-svg.d.ts.map

@@ -14,3 +14,3 @@ /**

*/
import { directive } from '../lit-html.js';
import { directive } from '../directive.js';
import { UnsafeHTML } from './unsafe-html.js';

@@ -17,0 +17,0 @@ const SVG_RESULT = 2;

@@ -14,12 +14,45 @@ /**

*/
import { Directive } from './directive.js';
/**
* Used to sanitize any value before it is written into the DOM. This can be
* used to implement a security policy of allowed and disallowed values in
* order to prevent XSS attacks.
*
* One way of using this callback would be to check attributes and properties
* against a list of high risk fields, and require that values written to such
* fields be instances of a class which is safe by construction. Closure's Safe
* HTML Types is one implementation of this technique (
* https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md).
* The TrustedTypes polyfill in API-only mode could also be used as a basis
* for this technique (https://github.com/WICG/trusted-types).
*
* @param node The HTML node (usually either a #text node or an Element) that
* is being written to. Note that this is just an exemplar node, the write
* may take place against another instance of the same class of node.
* @param name The name of an attribute or property (for example, 'href').
* @param type Indicates whether the write that's about to be performed will
* be to a property or a node.
* @return A function that will sanitize this class of writes.
*/
export declare type SanitizerFactory = (node: Node, name: string, type: 'property' | 'attribute') => ValueSanitizer;
/**
* A function which can sanitize values that will be written to a specific kind
* of DOM sink.
*
* See SanitizerFactory.
*
* @param value The value to sanitize. Will be the actual value passed into
* the lit-html template literal, so this could be of any type.
* @return The value to write to the DOM. Usually the same as the input value,
* unless sanitization is needed.
*/
export declare type ValueSanitizer = (value: unknown) => unknown;
/** TemplateResult types */
declare const HTML_RESULT = 1;
declare const SVG_RESULT = 2;
/** TemplatePart types */
export declare const ATTRIBUTE_PART = 1;
export declare const NODE_PART = 2;
export declare const PROPERTY_PART = 3;
export declare const BOOLEAN_ATTRIBUTE_PART = 4;
export declare const EVENT_PART = 5;
declare type ResultType = typeof HTML_RESULT | typeof SVG_RESULT;
declare const ATTRIBUTE_PART = 1;
declare const CHILD_PART = 2;
declare const ELEMENT_PART = 6;
declare const COMMENT_PART = 7;
/**

@@ -47,47 +80,7 @@ * The return type of the template tag functions.

*/
export declare const noChange: {};
export declare const noChange: unique symbol;
/**
* A sentinel value that signals a NodePart to fully clear its content.
* A sentinel value that signals a ChildPart to fully clear its content.
*/
export declare const nothing: {};
export declare type NodePartInfo = {
readonly type: typeof NODE_PART;
};
export declare type AttributePartInfo = {
readonly type: typeof ATTRIBUTE_PART | typeof PROPERTY_PART | typeof BOOLEAN_ATTRIBUTE_PART | typeof EVENT_PART;
strings?: ReadonlyArray<string>;
name: string;
tagName: string;
};
/**
* Information about the part a directive is bound to.
*
* This is useful for checking that a directive is attached to a valid part,
* such as with directive that can only be used on attribute bindings.
*/
export declare type PartInfo = NodePartInfo | AttributePartInfo;
export declare type DirectiveClass = {
new (part: PartInfo): Directive;
};
/**
* This utility type extracts the signature of a directive class's render()
* method so we can use it for the type of the generated directive function.
*/
export declare type DirectiveParameters<C extends DirectiveClass> = Parameters<InstanceType<C>['render']>;
/**
* A generated directive function doesn't evaluate the directive, but just
* returns a DirectiveResult object that captures the arguments.
*/
declare type DirectiveResult<C extends DirectiveClass = DirectiveClass> = {
_$litDirective$: C;
values: DirectiveParameters<C>;
};
/**
* Creates a user-facing directive function from a Directive class. This
* function has the same parameters as the directive's render() method.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
*/
export declare const directive: <C extends DirectiveClass>(c: C) => (...values: Parameters<InstanceType<C>["render"]>) => DirectiveResult<C>;
export declare const nothing: unique symbol;
export interface RenderOptions {

@@ -98,7 +91,7 @@ /**

*/
readonly eventContext?: EventTarget;
host?: EventTarget;
/**
* A DOM node before which to render content in the container.
*/
readonly renderBefore?: ChildNode | null;
renderBefore?: ChildNode | null;
}

@@ -111,37 +104,133 @@ /**

*/
export declare const render: (value: unknown, container: HTMLElement | DocumentFragment, options?: RenderOptions | undefined) => void;
export declare const render: {
(value: unknown, container: HTMLElement | DocumentFragment, options?: RenderOptions | undefined): ChildPart;
setSanitizer: (newSanitizer: SanitizerFactory) => void;
createSanitizer: SanitizerFactory;
_testOnlyClearSanitizerFactoryDoNotCallOrElse: () => void;
};
export interface DirectiveParent {
_$parent?: DirectiveParent;
__directive?: Directive;
__directives?: Array<Directive | undefined>;
}
export declare type Template = Interface<TemplateImpl>;
declare class TemplateImpl {
/** @internal */
_$element: HTMLTemplateElement;
/** @internal */
_parts: Array<TemplatePart>;
_$options?: RenderOptions;
constructor({ strings, _$litType$: type }: TemplateResult, options?: RenderOptions);
_$createElement(html: string): HTMLTemplateElement;
}
export interface Disconnectable {
_$parent?: Disconnectable;
_$disconnetableChildren?: Set<Disconnectable>;
}
declare function resolveDirective(part: ChildPart | AttributePart | ElementPart, value: unknown, _$parent?: DirectiveParent, _$attributeIndex?: number): unknown;
/**
* Base class for creating custom directives. Users should extend this class,
* implement `render` and/or `update`, and then pass their subclass to
* `directive`.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
* An updateable instance of a Template. Holds references to the Parts used to
* update the template instance.
*/
export declare abstract class Directive {
abstract render(...props: Array<unknown>): unknown;
update(_part: Part, props: Array<unknown>): unknown;
declare class TemplateInstance {
/** @internal */
_$template: Template;
/** @internal */
_parts: Array<Part | undefined>;
/** @internal */
_$parent: Disconnectable;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
constructor(template: Template, parent: ChildPart);
_clone(options: RenderOptions | undefined): DocumentFragment;
_update(values: Array<unknown>): void;
}
export declare type Part = NodePart | AttributePart | PropertyPart | BooleanAttributePart;
export declare class NodePart {
_startNode: ChildNode;
_endNode: ChildNode | null;
options: RenderOptions | undefined;
declare type AttributePartConstructor = {
new (element: HTMLElement, name: string, strings: ReadonlyArray<string>, parent: Disconnectable | undefined, options: RenderOptions | undefined): AttributePart;
};
declare type AttributeTemplatePart = {
readonly _type: typeof ATTRIBUTE_PART;
readonly _index: number;
readonly _name: string;
/** @internal */
readonly _constructor: AttributePartConstructor;
/** @internal */
readonly _strings: ReadonlyArray<string>;
};
declare type NodeTemplatePart = {
readonly _type: typeof CHILD_PART;
readonly _index: number;
};
declare type ElementTemplatePart = {
readonly _type: typeof ELEMENT_PART;
readonly _index: number;
};
declare type CommentTemplatePart = {
readonly _type: typeof COMMENT_PART;
readonly _index: number;
};
/**
* A TemplatePart represents a dynamic part in a template, before the template
* is instantiated. When a template is instantiated Parts are created from
* TemplateParts.
*/
declare type TemplatePart = NodeTemplatePart | AttributeTemplatePart | ElementTemplatePart | CommentTemplatePart;
export declare type Part = ChildPart | AttributePart | PropertyPart | BooleanAttributePart | ElementPart | EventPart;
declare type Interface<T> = {
[P in keyof T]: T[P];
};
export declare type ChildPart = Interface<ChildPartImpl>;
declare class ChildPartImpl {
readonly type = 2;
_value: unknown;
protected __directive?: Directive;
constructor(_startNode: ChildNode, _endNode: ChildNode | null, options: RenderOptions | undefined);
_setValue(value: unknown): void;
private __insert;
private __commitDirective;
readonly options: RenderOptions | undefined;
_$committedValue: unknown;
/** @internal */
__directive?: Directive;
/** @internal */
_$startNode: ChildNode;
/** @internal */
_$endNode: ChildNode | null;
private _textSanitizer;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
/** @internal */
_$setChildPartConnected?(isConnected: boolean, removeFromParent?: boolean, from?: number): void;
constructor(startNode: ChildNode, endNode: ChildNode | null, parent: TemplateInstance | ChildPart | undefined, options: RenderOptions | undefined);
/**
* Sets the connection state for any `DisconnectableDirectives` contained
* within this part and runs their `disconnectedCallback` or
* `reconnectedCallback`, according to the `isConnected` argument.
* @param isConnected
*/
setConnected(isConnected: boolean): void;
get parentNode(): Node;
_$setValue(value: unknown, directiveParent?: DirectiveParent): void;
private _insert;
private _commitNode;
private __commitText;
private __commitTemplateResult;
private __commitIterable;
__clear(start?: ChildNode | null): void;
private _commitText;
private _commitTemplateResult;
/** @internal */
_$getTemplate(strings: TemplateStringsArray, result: TemplateResult): Interface<TemplateImpl>;
private _commitIterable;
/**
* Removes the nodes contained within this Part from the DOM.
*
* @param start Start node to clear from, for clearing a subset of the part's
* DOM (used when truncating iterables)
* @param from When `start` is specified, the index within the iterable from
* which ChildParts are being removed, used for disconnecting directives in
* those Parts.
*
* @internal
*/
_$clear(start?: ChildNode | null, from?: number): void;
}
export declare class AttributePart {
export declare type AttributePart = Interface<AttributePartImpl>;
declare class AttributePartImpl {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: RenderOptions | undefined;
/**

@@ -153,24 +242,23 @@ * If this attribute part represents an interpolation, this contains the

readonly strings?: ReadonlyArray<string>;
_value: unknown | Array<unknown>;
private __directives?;
/** @internal */
_$committedValue: unknown | Array<unknown>;
/** @internal */
__directives?: Array<Directive | undefined>;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
protected _sanitizer: ValueSanitizer | undefined;
/** @internal */
_setDirectiveConnected?: (directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean) => void;
get tagName(): string;
constructor(element: HTMLElement, name: string, strings: ReadonlyArray<string>, _options?: RenderOptions);
constructor(element: HTMLElement, name: string, strings: ReadonlyArray<string>, parent: Disconnectable | undefined, options: RenderOptions | undefined);
/**
* Normalizes a user-provided value before writing it to the DOM. In the
* near future this will include invoking a directive if the value is
* a DirectiveResult.
*
* @param value the raw input value to normalize
* @param _i the index in the values array this value was read from
*/
__resolveValue(value: unknown, i: number): unknown;
/**
* Sets the value of this part.
*
* If this part is single-valued, `this.__strings` will be undefined, and the
* Sets the value of this part by resolving the value from possibly multiple
* values and static strings and committing it to the DOM.
* If this part is single-valued, `this._strings` will be undefined, and the
* method will be called with a single value argument. If this part is
* multi-value, `this.__strings` will be defined, and the method is called
* multi-value, `this._strings` will be defined, and the method is called
* with the value array of the part's owning TemplateInstance, and an offset
* into the value array from which the values should be read.
*
* This method is overloaded this way to eliminate short-lived array slices

@@ -181,20 +269,26 @@ * of the template instance values, and allow a fast-path for single-valued

* @param value The part value, or an array of values for multi-valued parts
* @param from the index to start reading values from. `undefined` for
* @param valueIndex the index to start reading values from. `undefined` for
* single-valued parts
* @param noCommit causes the part to not commit its value to the DOM. Used
* in hydration to prime attribute parts with their first-rendered value,
* but not set the attribute, and in SSR to no-op the DOM operation and
* capture the value for serialization.
*
* @internal
*/
_setValue(value: unknown): void;
_setValue(value: Array<unknown>, from: number): void;
/**
* Writes the value to the DOM. An override point for PropertyPart and
* BooleanAttributePart.
*/
__commitValue(value: unknown): void;
_$setValue(value: unknown | Array<unknown>, directiveParent?: DirectiveParent, valueIndex?: number, noCommit?: boolean): void;
/** @internal */
_commitValue(value: unknown): void;
}
export declare class PropertyPart extends AttributePart {
export declare type PropertyPart = Interface<PropertyPartImpl>;
declare class PropertyPartImpl extends AttributePartImpl {
readonly type = 3;
__commitValue(value: unknown): void;
/** @internal */
_commitValue(value: unknown): void;
}
export declare class BooleanAttributePart extends AttributePart {
export declare type BooleanAttributePart = Interface<BooleanAttributePartImpl>;
declare class BooleanAttributePartImpl extends AttributePartImpl {
readonly type = 4;
__commitValue(value: unknown): void;
/** @internal */
_commitValue(value: unknown): void;
}

@@ -212,10 +306,57 @@ /**

*/
export declare class EventPart extends AttributePart {
export declare type EventPart = Interface<EventPartImpl>;
declare class EventPartImpl extends AttributePartImpl {
readonly type = 5;
__eventContext?: unknown;
constructor(...args: ConstructorParameters<typeof AttributePart>);
_setValue(newListener: unknown): void;
/** @internal */
_$setValue(newListener: unknown, directiveParent?: DirectiveParent): void;
handleEvent(event: Event): void;
}
export declare type ElementPart = Interface<ElementPartImpl>;
declare class ElementPartImpl {
element: Element;
readonly type = 6;
/** @internal */
__directive?: Directive;
_$committedValue: undefined;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
/** @internal */
_setDirectiveConnected?: (directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean) => void;
options: RenderOptions | undefined;
constructor(element: Element, parent: Disconnectable, options: RenderOptions | undefined);
_$setValue(value: unknown): void;
}
/**
* END USERS SHOULD NOT RELY ON THIS OBJECT.
*
* Private exports for use by other Lit packages, not intended for use by
* external users.
*
* We currently do not make a mangled rollup build of the lit-ssr code. In order
* to keep a number of (otherwise private) top-level exports mangled in the
* client side code, we export a _$private object containing those members (or
* helper methods for accessing private fields of those members), and then
* re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
* client-side code is being used in `dev` mode or `prod` mode.
*
* @private
*/
export declare const _$private: {
_boundAttributeSuffix: string;
_marker: string;
_markerMatch: string;
_HTML_RESULT: number;
_getTemplateHtml: (strings: TemplateStringsArray, type: ResultType) => [string, string[]];
_TemplateInstance: typeof TemplateInstance;
_isIterable: (value: unknown) => value is Iterable<unknown>;
_resolveDirective: typeof resolveDirective;
_ChildPart: typeof ChildPartImpl;
_AttributePart: AttributePartConstructor;
_BooleanAttributePart: AttributePartConstructor;
_EventPart: AttributePartConstructor;
_PropertyPart: AttributePartConstructor;
};
export {};
//# sourceMappingURL=lit-html.d.ts.map

@@ -15,7 +15,29 @@ /**

var _a;
var _b;
const DEV_MODE = true;
const ENABLE_EXTRA_SECURITY_HOOKS = true;
if (DEV_MODE) {
console.warn('lit-html is in dev mode. Not recommended for production!');
}
const identityFunction = (value) => value;
const noopSanitizer = (_node, _name, _type) => identityFunction;
/** Sets the global sanitizer factory. */
const setSanitizer = (newSanitizer) => {
if (!ENABLE_EXTRA_SECURITY_HOOKS) {
return;
}
if (sanitizerFactoryInternal !== noopSanitizer) {
throw new Error(`Attempted to overwrite existing lit-html security policy.` +
` setSanitizeDOMValueFactory should be called at most once.`);
}
sanitizerFactoryInternal = newSanitizer;
};
/**
* Only used in internal tests, not a part of the public API.
*/
const _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => {
sanitizerFactoryInternal = noopSanitizer;
};
const createSanitizer = (node, name, type) => {
return sanitizerFactoryInternal(node, name, type);
};
// Added to an attribute name to mark the attribute as bound so we can find

@@ -40,3 +62,4 @@ // it easily.

const isIterable = (value) => isArray(value) ||
(value && typeof value[Symbol.iterator] === 'function');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof value?.[Symbol.iterator] === 'function';
// TODO (justinfagnani): can we get away with `\s`?

@@ -73,3 +96,4 @@ const SPACE_CHAR = `[ \t\n\f\r]`;

* The tagEnd regex matches the end of the "inside an opening" tag syntax
* position. It either matches a `>` or an attribute.
* position. It either matches a `>`, an attribute-like sequence, or the end
* of the string after a space (attribute-name position ending).
*

@@ -96,3 +120,3 @@ * See attributes in the HTML spec:

*/
const tagEndRegex = new RegExp(`>|${SPACE_CHAR}(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))`, 'g');
const tagEndRegex = new RegExp(`>|${SPACE_CHAR}(?:(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))|$)`, 'g');
const ENTIRE_MATCH = 0;

@@ -114,10 +138,9 @@ const ATTRIBUTE_NAME = 1;

const SVG_RESULT = 2;
/** TemplatePart types */
// TODO (justinfagnani): since these are exported, consider shorter names,
// like just `ATTRIBUTE`.
export const ATTRIBUTE_PART = 1;
export const NODE_PART = 2;
export const PROPERTY_PART = 3;
export const BOOLEAN_ATTRIBUTE_PART = 4;
export const EVENT_PART = 5;
// TemplatePart types
// IMPORTANT: these must match the values in PartType
const ATTRIBUTE_PART = 1;
const CHILD_PART = 2;
const PROPERTY_PART = 3;
const BOOLEAN_ATTRIBUTE_PART = 4;
const EVENT_PART = 5;
const ELEMENT_PART = 6;

@@ -148,7 +171,7 @@ const COMMENT_PART = 7;

*/
export const noChange = {};
export const noChange = Symbol.for('lit-noChange');
/**
* A sentinel value that signals a NodePart to fully clear its content.
* A sentinel value that signals a ChildPart to fully clear its content.
*/
export const nothing = {};
export const nothing = Symbol.for('lit-nothing');
/**

@@ -163,13 +186,2 @@ * The cache of prepared templates, keyed by the tagged TemplateStringsArray

/**
* Creates a user-facing directive function from a Directive class. This
* function has the same parameters as the directive's render() method.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
*/
export const directive = (c) => (...values) => ({
_$litDirective$: c,
values,
});
/**
* Renders a value, usually a lit-html TemplateResult, to the container.

@@ -181,170 +193,188 @@ * @param value

export const render = (value, container, options) => {
var _a, _b;
const partOwnerNode = (_a = options === null || options === void 0 ? void 0 : options.renderBefore) !== null && _a !== void 0 ? _a : container;
let part = partOwnerNode.$lit$;
const partOwnerNode = options?.renderBefore ?? container;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let part = partOwnerNode._$litPart;
if (part === undefined) {
const endNode = (_b = options === null || options === void 0 ? void 0 : options.renderBefore) !== null && _b !== void 0 ? _b : null;
partOwnerNode.$lit$ = part = new NodePart(container.insertBefore(createMarker(), endNode), endNode, options);
const endNode = options?.renderBefore ?? null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
partOwnerNode._$litPart = part = new ChildPartImpl(container.insertBefore(createMarker(), endNode), endNode, undefined, options);
}
part._setValue(value);
part._$setValue(value);
return part;
};
const walker = d.createTreeWalker(d);
//
// Classes only below here, const variable declarations only above here...
//
// Keeping variable declarations and classes together improves minification.
// Interfaces and type aliases can be interleaved freely.
//
if (ENABLE_EXTRA_SECURITY_HOOKS) {
render.setSanitizer = setSanitizer;
render.createSanitizer = createSanitizer;
if (DEV_MODE) {
render._testOnlyClearSanitizerFactoryDoNotCallOrElse = _testOnlyClearSanitizerFactoryDoNotCallOrElse;
}
}
const walker = d.createTreeWalker(d, 133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */, null, false);
let sanitizerFactoryInternal = noopSanitizer;
/**
* Base class for creating custom directives. Users should extend this class,
* implement `render` and/or `update`, and then pass their subclass to
* `directive`.
* Returns an HTML string for the given TemplateStringsArray and result type
* (HTML or SVG), along with the case-sensitive bound attribute names in
* template order. The HTML contains comment comment markers denoting the
* `ChildPart`s and suffixes on bound attributes denoting the `AttributeParts`.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
* @param strings template strings array
* @param type HTML or SVG
* @return Array containing `[html, attrNames]` (array returned for terseness,
* to avoid object fields since this code is shared with non-minified SSR
* code)
*/
export class Directive {
update(_part, props) {
return this.render(...props);
}
}
class Template {
constructor({ strings, _$litType$: type }) {
this.__parts = [];
walker.currentNode = (this.__element = d.createElement('template')).content;
// Insert makers into the template HTML to represent the position of
// bindings. The following code scans the template strings to determine the
// syntactic position of the bindings. They can be in text position, where
// we insert an HTML comment, attribute value position, where we insert a
// sentinel string and re-write the attribute name, or inside a tag where
// we insert the sentinel string.
const l = (this.__strings = strings).length - 1;
const attrNames = [];
let html = type === SVG_RESULT ? '<svg>' : '';
let node;
let nodeIndex = 0;
let bindingIndex = 0;
let attrNameIndex = 0;
// When we're inside a raw text tag (not it's text content), the regex
// will still be tagRegex so we can find attributes, but will switch to
// this regex when the tag ends.
let rawTextEndRegex;
// The current parsing state, represented as a reference to one of the
// regexes
let regex = textEndRegex;
for (let i = 0; i < l; i++) {
const s = strings[i];
// The index of the end of the last attribute name. When this is
// positive at end of a string, it means we're in an attribute value
// position and need to rewrite the attribute name.
let attrNameEndIndex = -1;
let attrName;
let lastIndex = 0;
let match;
// The conditions in this loop handle the current parse state, and the
// assignments to the `regex` variable are the state transitions.
while (lastIndex < s.length) {
// Make sure we start searching from where we previously left off
regex.lastIndex = lastIndex;
match = regex.exec(s);
if (match === null) {
// If the current regex doesn't match we've come to a binding inside
// that state and must break and insert a marker
if (regex === tagEndRegex) {
// When tagEndRegex doesn't match we must have a binding in
// attribute-name position, since tagEndRegex does match static
// attribute names and end-of-tag. We need to clear
// attrNameEndIndex which may have been set by a previous
// tagEndRegex match.
attrNameEndIndex = -1;
}
break;
const getTemplateHtml = (strings, type) => {
// Insert makers into the template HTML to represent the position of
// bindings. The following code scans the template strings to determine the
// syntactic position of the bindings. They can be in text position, where
// we insert an HTML comment, attribute value position, where we insert a
// sentinel string and re-write the attribute name, or inside a tag where
// we insert the sentinel string.
const l = strings.length - 1;
const attrNames = [];
let html = type === SVG_RESULT ? '<svg>' : '';
// When we're inside a raw text tag (not it's text content), the regex
// will still be tagRegex so we can find attributes, but will switch to
// this regex when the tag ends.
let rawTextEndRegex;
// The current parsing state, represented as a reference to one of the
// regexes
let regex = textEndRegex;
for (let i = 0; i < l; i++) {
const s = strings[i];
// The index of the end of the last attribute name. When this is
// positive at end of a string, it means we're in an attribute value
// position and need to rewrite the attribute name.
// We also use a special value of -2 to indicate that we encountered
// the end of a string in attribute name position.
let attrNameEndIndex = -1;
let attrName;
let lastIndex = 0;
let match;
// The conditions in this loop handle the current parse state, and the
// assignments to the `regex` variable are the state transitions.
while (lastIndex < s.length) {
// Make sure we start searching from where we previously left off
regex.lastIndex = lastIndex;
match = regex.exec(s);
if (match === null) {
break;
}
lastIndex = regex.lastIndex;
if (regex === textEndRegex) {
if (match[COMMENT_START] === '!--') {
regex = commentEndRegex;
}
lastIndex = regex.lastIndex;
if (regex === textEndRegex) {
if (match[COMMENT_START] === '!--') {
regex = commentEndRegex;
}
else if (match[COMMENT_START] !== undefined) {
// We started a weird comment, like </{
regex = comment2EndRegex;
}
else if (match[TAG_NAME] !== undefined) {
if (rawTextElement.test(match[TAG_NAME])) {
// Record if we encounter a raw-text element. We'll switch to
// this regex at the end of the tag
rawTextEndRegex = new RegExp(`</${match[TAG_NAME]}`, 'g');
}
regex = tagEndRegex;
}
else if (match[DYNAMIC_TAG_NAME] !== undefined) {
// dynamic tag name
regex = tagEndRegex;
}
else if (match[COMMENT_START] !== undefined) {
// We started a weird comment, like </{
regex = comment2EndRegex;
}
else if (regex === tagEndRegex) {
if (match[ENTIRE_MATCH] === '>') {
// End of a tag. If we had started a raw-text element, use that
// regex
regex = rawTextEndRegex !== null && rawTextEndRegex !== void 0 ? rawTextEndRegex : textEndRegex;
// We may be ending an unquoted attribute value, so make sure we
// clear any pending attrNameEndIndex
attrNameEndIndex = -1;
else if (match[TAG_NAME] !== undefined) {
if (rawTextElement.test(match[TAG_NAME])) {
// Record if we encounter a raw-text element. We'll switch to
// this regex at the end of the tag.
rawTextEndRegex = new RegExp(`</${match[TAG_NAME]}`, 'g');
}
else {
attrNameEndIndex =
regex.lastIndex - match[SPACES_AND_EQUALS].length;
attrName = match[ATTRIBUTE_NAME];
regex =
match[QUOTE_CHAR] === undefined
? tagEndRegex
: match[QUOTE_CHAR] === '"'
? doubleQuoteAttrEndRegex
: singleQuoteAttrEndRegex;
}
regex = tagEndRegex;
}
else if (regex === doubleQuoteAttrEndRegex ||
regex === singleQuoteAttrEndRegex) {
else if (match[DYNAMIC_TAG_NAME] !== undefined) {
// dynamic tag name
regex = tagEndRegex;
}
else if (regex === commentEndRegex || regex === comment2EndRegex) {
regex = textEndRegex;
}
else if (regex === tagEndRegex) {
if (match[ENTIRE_MATCH] === '>') {
// End of a tag. If we had started a raw-text element, use that
// regex
regex = rawTextEndRegex ?? textEndRegex;
// We may be ending an unquoted attribute value, so make sure we
// clear any pending attrNameEndIndex
attrNameEndIndex = -1;
}
else if (match[ATTRIBUTE_NAME] === undefined) {
// Attribute name position
attrNameEndIndex = -2;
}
else {
// Not one of the five state regexes, so it must be the dynamically
// created raw text regex and we're at the close of that element.
regex = tagEndRegex;
rawTextEndRegex = undefined;
attrNameEndIndex = regex.lastIndex - match[SPACES_AND_EQUALS].length;
attrName = match[ATTRIBUTE_NAME];
regex =
match[QUOTE_CHAR] === undefined
? tagEndRegex
: match[QUOTE_CHAR] === '"'
? doubleQuoteAttrEndRegex
: singleQuoteAttrEndRegex;
}
}
if (DEV_MODE) {
// If we have a attrNameEndIndex, which indicates that we should
// rewrite the attribute name, assert that we're in a valid attribute
// position - either in a tag, or a quoted attribute value.
console.assert(attrNameEndIndex === -1 ||
regex === tagEndRegex ||
regex === singleQuoteAttrEndRegex ||
regex === doubleQuoteAttrEndRegex, 'unexpected parse state B');
else if (regex === doubleQuoteAttrEndRegex ||
regex === singleQuoteAttrEndRegex) {
regex = tagEndRegex;
}
// If we're in text position, and not in a raw text element
// (regex === textEndRegex), we insert a comment marker. Otherwise, we
// insert a plain maker. If we have a attrNameEndIndex, it means we need
// to rewrite the attribute name to add a bound attribute suffix.
html +=
regex === textEndRegex
? s + nodeMarker
: (attrNameEndIndex !== -1
? (attrNames.push(attrName),
s.slice(0, attrNameEndIndex) +
boundAttributeSuffix +
s.slice(attrNameEndIndex))
: s) + marker;
else if (regex === commentEndRegex || regex === comment2EndRegex) {
regex = textEndRegex;
}
else {
// Not one of the five state regexes, so it must be the dynamically
// created raw text regex and we're at the close of that element.
regex = tagEndRegex;
rawTextEndRegex = undefined;
}
}
// TODO (justinfagnani): if regex is not textRegex log a warning for a
// malformed template in dev mode.
// Note, we don't add '</svg>' for SVG result types because the parser
// will close the <svg> tag for us.
this.__element.innerHTML = html + this.__strings[l];
if (DEV_MODE) {
// If we have a attrNameEndIndex, which indicates that we should
// rewrite the attribute name, assert that we're in a valid attribute
// position - either in a tag, or a quoted attribute value.
console.assert(attrNameEndIndex === -1 ||
regex === tagEndRegex ||
regex === singleQuoteAttrEndRegex ||
regex === doubleQuoteAttrEndRegex, 'unexpected parse state B');
}
// We have four cases:
// 1. We're in text position, and not in a raw text element
// (regex === textEndRegex): insert a comment marker.
// 2. We have a non-negative attrNameEndIndex which means we need to
// rewrite the attribute name to add a bound attribute suffix.
// 3. We're at the non-first binding in a multi-binding attribute, use a
// plain marker.
// 4. We're somewhere else inside the tag. If we're in attribute name
// position (attrNameEndIndex === -2), add a sequential suffix to
// generate a unique attribute name.
html +=
regex === textEndRegex
? s + nodeMarker
: attrNameEndIndex >= 0
? (attrNames.push(attrName),
s.slice(0, attrNameEndIndex) +
boundAttributeSuffix +
s.slice(attrNameEndIndex)) + marker
: s + marker + (attrNameEndIndex === -2 ? `:${i}` : '');
}
// Returned as an array for terseness
return [
// We don't technically need to close the SVG tag since the parser will
// handle it for us, but the SSR parser doesn't like that.
// Note that the html must end with a node after the final expression to
// ensure the last ChildPart has an end node, hence adding a comment if the
// last string was empty.
html + (strings[l] || '<?>') + (type === SVG_RESULT ? '</svg>' : ''),
attrNames,
];
};
class TemplateImpl {
constructor({ strings, _$litType$: type }, options) {
/** @internal */
this._parts = [];
this._$options = options;
let node;
let nodeIndex = 0;
let bindingIndex = 0;
let attrNameIndex = 0;
const l = strings.length - 1;
// Create template element
const [html, attrNames] = getTemplateHtml(strings, type);
this._$element = this._$createElement(html);
walker.currentNode = this._$element.content;
// Reparent SVG nodes into template root
if (type === SVG_RESULT) {
const content = this.__element.content;
const content = this._$element.content;
const svgElement = content.firstChild;

@@ -361,34 +391,47 @@ svgElement.remove();

if (node.hasAttributes()) {
const { attributes } = node;
for (let i = 0; i < attributes.length; i++) {
const { name, value } = attributes[i];
// We defer removing bound attributes because on IE we might not be
// iterating attributes in their template order, and would sometimes
// remove an attribute that we still need to create a part for.
const attrsToRemove = [];
for (const name of node.getAttributeNames()) {
// `name` is the name of the attribute we're iterating over, but not
// _neccessarily_ the name of the attribute we will create a part
// for. They can be different in browsers that don't iterate on
// attributes in source order. In that case the attrNames array
// contains the attribute name we'll process next. We only need the
// attribute name here to know if we should process a bound attribute
// on this element.
if (name.endsWith(boundAttributeSuffix)) {
i--;
node.removeAttribute(name);
const realName = attrNames[attrNameIndex++];
// Lowercase for case-sensitive SVG attributes like viewBox
const value = node.getAttribute(realName.toLowerCase() + boundAttributeSuffix);
attrsToRemove.push(name);
const statics = value.split(marker);
const m = /([.?@])?(.*)/.exec(attrNames[attrNameIndex++]);
this.__parts.push({
__type: ATTRIBUTE_PART,
__index: nodeIndex,
__name: m[2],
__strings: statics,
__constructor: m[1] === '.'
? PropertyPart
const m = /([.?@])?(.*)/.exec(realName);
this._parts.push({
_type: ATTRIBUTE_PART,
_index: nodeIndex,
_name: m[2],
_strings: statics,
_constructor: m[1] === '.'
? PropertyPartImpl
: m[1] === '?'
? BooleanAttributePart
? BooleanAttributePartImpl
: m[1] === '@'
? EventPart
: AttributePart,
? EventPartImpl
: AttributePartImpl,
});
bindingIndex += statics.length - 1;
}
else if (name === marker) {
node.removeAttribute(name);
i--;
this.__parts.push({
__type: ELEMENT_PART,
__index: nodeIndex,
else if (name.startsWith(marker)) {
attrsToRemove.push(name);
this._parts.push({
_type: ELEMENT_PART,
_index: nodeIndex,
});
}
}
for (const name of attrsToRemove) {
node.removeAttribute(name);
}
}

@@ -411,3 +454,3 @@ // TODO (justinfagnani): benchmark the regex against testing for each

node.append(strings[i] || createMarker());
this.__parts.push({ __type: NODE_PART, __index: ++nodeIndex });
this._parts.push({ _type: CHILD_PART, _index: ++nodeIndex });
bindingIndex++;

@@ -423,3 +466,3 @@ }

bindingIndex++;
this.__parts.push({ __type: NODE_PART, __index: nodeIndex });
this._parts.push({ _type: CHILD_PART, _index: nodeIndex });
}

@@ -433,3 +476,3 @@ else {

// make bindings in comments work
this.__parts.push({ __type: COMMENT_PART, __index: nodeIndex });
this._parts.push({ _type: COMMENT_PART, _index: nodeIndex });
bindingIndex++;

@@ -444,3 +487,40 @@ // Move to the end of the match

}
// Overridden via `litHtmlPlatformSupport` to provide platform support.
_$createElement(html) {
const template = d.createElement('template');
template.innerHTML = html;
return template;
}
}
function resolveDirective(part, value, _$parent = part, _$attributeIndex) {
var _a;
let currentDirective = _$attributeIndex !== undefined
? _$parent.__directives?.[_$attributeIndex]
: _$parent.__directive;
const nextDirectiveConstructor = isPrimitive(value)
? undefined
: value._$litDirective$;
if (currentDirective?.constructor !== nextDirectiveConstructor) {
currentDirective?._$setDirectiveConnected?.(false);
currentDirective =
nextDirectiveConstructor === undefined
? undefined
: new nextDirectiveConstructor({
...part,
_$part: part,
_$parent,
_$attributeIndex,
});
if (_$attributeIndex !== undefined) {
((_a = _$parent).__directives ?? (_a.__directives = []))[_$attributeIndex] = currentDirective;
}
else {
_$parent.__directive = currentDirective;
}
}
if (currentDirective !== undefined) {
value = currentDirective._resolve(value.values);
}
return value;
}
/**

@@ -451,10 +531,14 @@ * An updateable instance of a Template. Holds references to the Parts used to

class TemplateInstance {
constructor(template) {
this.__parts = [];
this.__template = template;
constructor(template, parent) {
/** @internal */
this._parts = [];
/** @internal */
this._$disconnetableChildren = undefined;
this._$template = template;
this._$parent = parent;
}
// This method is separate from the constructor because we need to return a
// DocumentFragment and we don't want to hold onto it with an instance field.
__clone(options) {
const { __element: { content }, __parts: parts, } = this.__template;
_clone(options) {
const { _$element: { content }, _parts: parts, } = this._$template;
const fragment = d.importNode(content, true);

@@ -467,14 +551,17 @@ walker.currentNode = fragment;

while (templatePart !== undefined && node !== null) {
if (nodeIndex === templatePart.__index) {
if (nodeIndex === templatePart._index) {
let part;
if (templatePart.__type === NODE_PART) {
part = new NodePart(node, node.nextSibling, options);
if (templatePart._type === CHILD_PART) {
part = new ChildPartImpl(node, node.nextSibling, this, options);
}
else if (templatePart.__type === ATTRIBUTE_PART) {
part = new templatePart.__constructor(node, templatePart.__name, templatePart.__strings, options);
else if (templatePart._type === ATTRIBUTE_PART) {
part = new templatePart._constructor(node, templatePart._name, templatePart._strings, this, options);
}
this.__parts.push(part);
else if (templatePart._type === ELEMENT_PART) {
part = new ElementPartImpl(node, this, options);
}
this._parts.push(part);
templatePart = parts[++partIndex];
}
if (templatePart !== undefined && nodeIndex !== templatePart.__index) {
if (templatePart !== undefined && nodeIndex !== templatePart._index) {
node = walker.nextNode();

@@ -486,41 +573,63 @@ nodeIndex++;

}
__update(values) {
_update(values) {
let i = 0;
for (const part of this.__parts) {
if (part === undefined) {
i++;
continue;
for (const part of this._parts) {
if (part !== undefined) {
if (part.strings !== undefined) {
part._$setValue(values, part, i);
// The number of values the part consumes is part.strings.length - 1
// since values are in between template spans. We increment i by 1
// later in the loop, so increment it by part.strings.length - 2 here
i += part.strings.length - 2;
}
else {
part._$setValue(values[i]);
}
}
if (part.strings !== undefined) {
part._setValue(values, i);
i += part.strings.length - 1;
}
else {
part._setValue(values[i++]);
}
i++;
}
}
}
export class NodePart {
constructor(_startNode, _endNode, options) {
this._startNode = _startNode;
this._endNode = _endNode;
class ChildPartImpl {
constructor(startNode, endNode, parent, options) {
this.type = CHILD_PART;
// The following fields will be patched onto ChildParts when required by
// DisconnectableDirective
/** @internal */
this._$disconnetableChildren = undefined;
this._$startNode = startNode;
this._$endNode = endNode;
this._$parent = parent;
this.options = options;
this.type = NODE_PART;
if (ENABLE_EXTRA_SECURITY_HOOKS) {
// Explicitly initialize for consistent class shape.
this._textSanitizer = undefined;
}
}
_setValue(value) {
// TODO (justinfagnani): when setting a non-directive over a directive,
// we don't yet clear this.__directive.
// See https://github.com/Polymer/lit-html/issues/1286
/**
* Sets the connection state for any `DisconnectableDirectives` contained
* within this part and runs their `disconnectedCallback` or
* `reconnectedCallback`, according to the `isConnected` argument.
* @param isConnected
*/
setConnected(isConnected) {
this._$setChildPartConnected?.(isConnected);
}
get parentNode() {
return this._$startNode.parentNode;
}
_$setValue(value, directiveParent = this) {
value = resolveDirective(this, value, directiveParent);
if (isPrimitive(value)) {
if (value !== this._value) {
this.__commitText(value);
if (value === nothing) {
this._$clear();
this._$committedValue = nothing;
}
else if (value !== this._$committedValue && value !== noChange) {
this._commitText(value);
}
}
else if (value._$litType$ !== undefined) {
this.__commitTemplateResult(value);
this._commitTemplateResult(value);
}
else if (value._$litDirective$ !== undefined) {
this.__commitDirective(value);
}
else if (value.nodeType !== undefined) {

@@ -530,45 +639,44 @@ this._commitNode(value);

else if (isIterable(value)) {
this.__commitIterable(value);
this._commitIterable(value);
}
else if (value === nothing) {
this._value = nothing;
this.__clear();
}
else if (value !== noChange) {
else {
// Fallback, will render the string representation
this.__commitText(value);
this._commitText(value);
}
}
__insert(node, ref = this._endNode) {
return this._startNode.parentNode.insertBefore(node, ref);
_insert(node, ref = this._$endNode) {
return this._$startNode.parentNode.insertBefore(node, ref);
}
__commitDirective(value) {
var _a;
const directive = value._$litDirective$;
if (((_a = this.__directive) === null || _a === void 0 ? void 0 : _a.constructor) !== directive) {
this.__clear();
this.__directive = new directive(this);
}
// TODO (justinfagnani): To support nested directives, we'd need to
// resolve the directive result's values. We may want to offer another
// way of composing directives.
this._setValue(this.__directive.update(this, value.values));
}
_commitNode(value) {
if (this._value !== value) {
this.__clear();
this._value = this.__insert(value);
if (this._$committedValue !== value) {
this._$clear();
if (ENABLE_EXTRA_SECURITY_HOOKS &&
sanitizerFactoryInternal !== noopSanitizer) {
const parentNodeName = this._$startNode.parentNode?.nodeName;
if (parentNodeName === 'STYLE' || parentNodeName === 'SCRIPT') {
this._insert(new Text('/* lit-html will not write ' +
'TemplateResults to scripts and styles */'));
return;
}
}
this._$committedValue = this._insert(value);
}
}
__commitText(value) {
const node = this._startNode.nextSibling;
_commitText(value) {
const node = this._$startNode.nextSibling;
// Make sure undefined and null render as an empty string
// TODO: use `nothing` to clear the node?
value !== null && value !== void 0 ? value : (value = '');
// TODO(justinfagnani): Can we just check if this._value is primitive?
value ?? (value = '');
// TODO(justinfagnani): Can we just check if this._$committedValue is primitive?
if (node !== null &&
node.nodeType === 3 /* Node.TEXT_NODE */ &&
(this._endNode === null
(this._$endNode === null
? node.nextSibling === null
: node === this._endNode.previousSibling)) {
: node === this._$endNode.previousSibling)) {
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._textSanitizer === undefined) {
this._textSanitizer = createSanitizer(node, 'data', 'property');
}
value = this._textSanitizer(value);
}
// If we only have a single text node between the markers, we can just

@@ -579,25 +687,45 @@ // set its value, rather than replacing it.

else {
this._commitNode(new Text(value));
if (ENABLE_EXTRA_SECURITY_HOOKS) {
const textNode = document.createTextNode('');
this._commitNode(textNode);
// When setting text content, for security purposes it matters a lot
// what the parent is. For example, <style> and <script> need to be
// handled with care, while <span> does not. So first we need to put a
// text node into the document, then we can sanitize its contentx.
if (this._textSanitizer === undefined) {
this._textSanitizer = createSanitizer(textNode, 'data', 'property');
}
value = this._textSanitizer(value);
textNode.data = value;
}
else {
this._commitNode(d.createTextNode(value));
}
}
this._value = value;
this._$committedValue = value;
}
__commitTemplateResult(result) {
const { strings, values } = result;
let template = templateCache.get(strings);
if (template === undefined) {
templateCache.set(strings, (template = new Template(result)));
_commitTemplateResult(result) {
const { values, strings } = result;
const template = this._$getTemplate(strings, result);
if (this._$committedValue?._$template === template) {
this._$committedValue._update(values);
}
if (this._value != null &&
this._value.__template === template) {
this._value.__update(values);
}
else {
const instance = new TemplateInstance(template);
const fragment = instance.__clone(this.options);
instance.__update(values);
const instance = new TemplateInstance(template, this);
const fragment = instance._clone(this.options);
instance._update(values);
this._commitNode(fragment);
this._value = instance;
this._$committedValue = instance;
}
}
__commitIterable(value) {
// Overridden via `litHtmlPlatformSupport` to provide platform support.
/** @internal */
_$getTemplate(strings, result) {
let template = templateCache.get(strings);
if (template === undefined) {
templateCache.set(strings, (template = new TemplateImpl(result)));
}
return template;
}
_commitIterable(value) {
// For an Iterable, we create a new InstancePart per item, then set its

@@ -609,12 +737,12 @@ // value to the item. This is a little bit of overhead for every item in

// If value is an array, then the previous render was of an
// iterable and value will contain the NodeParts from the previous
// iterable and value will contain the ChildParts from the previous
// render. If value is not an array, clear this part and make a new
// array for NodeParts.
if (!isArray(this._value)) {
this._value = [];
this.__clear();
// array for ChildParts.
if (!isArray(this._$committedValue)) {
this._$committedValue = [];
this._$clear();
}
// Lets us keep track of how many items we stamped so we can clear leftover
// items from a previous render
const itemParts = this._value;
const itemParts = this._$committedValue;
let partIndex = 0;

@@ -628,3 +756,3 @@ let itemPart;

// https://github.com/Polymer/lit-html/issues/1266
itemParts.push((itemPart = new NodePart(this.__insert(createMarker()), this.__insert(createMarker()), this.options)));
itemParts.push((itemPart = new ChildPartImpl(this._insert(createMarker()), this._insert(createMarker()), this, this.options)));
}

@@ -635,14 +763,26 @@ else {

}
itemPart._setValue(item);
itemPart._$setValue(item);
partIndex++;
}
if (partIndex < itemParts.length) {
// itemParts always have end nodes
this._$clear(itemPart?._$endNode.nextSibling, partIndex);
// Truncate the parts array so _value reflects the current state
itemParts.length = partIndex;
// itemParts always have end nodes
this.__clear(itemPart === null || itemPart === void 0 ? void 0 : itemPart._endNode.nextSibling);
}
}
__clear(start = this._startNode.nextSibling) {
while (start && start !== this._endNode) {
/**
* Removes the nodes contained within this Part from the DOM.
*
* @param start Start node to clear from, for clearing a subset of the part's
* DOM (used when truncating iterables)
* @param from When `start` is specified, the index within the iterable from
* which ChildParts are being removed, used for disconnecting directives in
* those Parts.
*
* @internal
*/
_$clear(start = this._$startNode.nextSibling, from) {
this._$setChildPartConnected?.(false, true, from);
while (start && start !== this._$endNode) {
const n = start.nextSibling;

@@ -654,15 +794,25 @@ start.remove();

}
export class AttributePart {
constructor(element, name, strings, _options) {
class AttributePartImpl {
constructor(element, name, strings, parent, options) {
this.type = ATTRIBUTE_PART;
this._value = nothing;
/** @internal */
this._$committedValue = nothing;
/** @internal */
this._$disconnetableChildren = undefined;
/** @internal */
this._setDirectiveConnected = undefined;
this.element = element;
this.name = name;
this._$parent = parent;
this.options = options;
if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') {
this._value = new Array(strings.length - 1).fill(nothing);
this._$committedValue = new Array(strings.length - 1).fill(nothing);
this.strings = strings;
}
else {
this._value = nothing;
this._$committedValue = nothing;
}
if (ENABLE_EXTRA_SECURITY_HOOKS) {
this._sanitizer = undefined;
}
}

@@ -673,37 +823,35 @@ get tagName() {

/**
* Normalizes a user-provided value before writing it to the DOM. In the
* near future this will include invoking a directive if the value is
* a DirectiveResult.
* Sets the value of this part by resolving the value from possibly multiple
* values and static strings and committing it to the DOM.
* If this part is single-valued, `this._strings` will be undefined, and the
* method will be called with a single value argument. If this part is
* multi-value, `this._strings` will be defined, and the method is called
* with the value array of the part's owning TemplateInstance, and an offset
* into the value array from which the values should be read.
* This method is overloaded this way to eliminate short-lived array slices
* of the template instance values, and allow a fast-path for single-valued
* parts.
*
* @param value the raw input value to normalize
* @param _i the index in the values array this value was read from
* @param value The part value, or an array of values for multi-valued parts
* @param valueIndex the index to start reading values from. `undefined` for
* single-valued parts
* @param noCommit causes the part to not commit its value to the DOM. Used
* in hydration to prime attribute parts with their first-rendered value,
* but not set the attribute, and in SSR to no-op the DOM operation and
* capture the value for serialization.
*
* @internal
*/
__resolveValue(value, i) {
var _a, _b;
const directiveCtor = (_a = value) === null || _a === void 0 ? void 0 : _a._$litDirective$;
if (directiveCtor !== undefined) {
// TODO (justinfagnani): Initialize array to the correct value,
// or check length.
let directive = ((_b = this.__directives) !== null && _b !== void 0 ? _b : (this.__directives = []))[i];
if ((directive === null || directive === void 0 ? void 0 : directive.constructor) !== directiveCtor) {
directive = this.__directives[i] = new directiveCtor(this);
}
// TODO (justinfagnani): To support nested directives, we'd need to
// resolve the directive result's values. We may want to offer another
// way of composing directives.
value = directive.update(this, value.values);
}
return value !== null && value !== void 0 ? value : '';
}
_setValue(value, from) {
_$setValue(value, directiveParent = this, valueIndex, noCommit) {
const strings = this.strings;
// Whether any of the values has changed, for dirty-checking
let change = false;
if (strings === undefined) {
// Single-value binding case
const v = this.__resolveValue(value, 0);
// Only dirty-check primitives and `nothing`:
// `(isPrimitive(v) || v === nothing)` limits the clause to primitives and
// `nothing`. `v === this._value` is the dirty-check.
if (!((isPrimitive(v) || v === nothing) && v === this._value) &&
v !== noChange) {
this.__commitValue((this._value = v));
value = resolveDirective(this, value, directiveParent, 0);
change =
!isPrimitive(value) ||
(value !== this._$committedValue && value !== noChange);
if (change) {
this._$committedValue = value;
}

@@ -713,36 +861,29 @@ }

// Interpolation case
let attributeValue = strings[0];
// Whether any of the values has changed, for dirty-checking
let change = false;
// Whether any of the values is the `nothing` sentinel. If any are, we
// remove the entire attribute.
let remove = false;
const values = value;
value = strings[0];
let i, v;
for (i = 0; i < strings.length - 1; i++) {
v = this.__resolveValue(value[from + i], i);
v = resolveDirective(this, values[valueIndex + i], directiveParent, i);
if (v === noChange) {
// If the user-provided value is `noChange`, use the previous value
v = this._value[i];
v = this._$committedValue[i];
}
else {
remove = remove || v === nothing;
change =
change ||
!((isPrimitive(v) || v === nothing) &&
v === this._value[i]);
this._value[i] = v;
change || (change = !isPrimitive(v) || v !== this._$committedValue[i]);
if (v === nothing) {
value = nothing;
}
attributeValue +=
(typeof v === 'string' ? v : String(v)) + strings[i + 1];
else if (value !== nothing) {
value += (v ?? '') + strings[i + 1];
}
// We always record each value, even if one is `nothing`, for future
// change detection.
this._$committedValue[i] = v;
}
if (change) {
this.__commitValue(remove ? nothing : attributeValue);
}
}
if (change && !noCommit) {
this._commitValue(value);
}
}
/**
* Writes the value to the DOM. An override point for PropertyPart and
* BooleanAttributePart.
*/
__commitValue(value) {
/** @internal */
_commitValue(value) {
if (value === nothing) {

@@ -752,7 +893,13 @@ this.element.removeAttribute(this.name);

else {
this.element.setAttribute(this.name, value);
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._sanitizer === undefined) {
this._sanitizer = sanitizerFactoryInternal(this.element, this.name, 'attribute');
}
value = this._sanitizer(value ?? '');
}
this.element.setAttribute(this.name, (value ?? ''));
}
}
}
export class PropertyPart extends AttributePart {
class PropertyPartImpl extends AttributePartImpl {
constructor() {

@@ -762,7 +909,15 @@ super(...arguments);

}
__commitValue(value) {
/** @internal */
_commitValue(value) {
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._sanitizer === undefined) {
this._sanitizer = sanitizerFactoryInternal(this.element, this.name, 'property');
}
value = this._sanitizer(value);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.element[this.name] = value === nothing ? undefined : value;
}
}
export class BooleanAttributePart extends AttributePart {
class BooleanAttributePartImpl extends AttributePartImpl {
constructor() {

@@ -772,3 +927,4 @@ super(...arguments);

}
__commitValue(value) {
/** @internal */
_commitValue(value) {
if (value && value !== nothing) {

@@ -782,23 +938,17 @@ this.element.setAttribute(this.name, '');

}
/**
* An AttributePart that manages an event listener via add/removeEventListener.
*
* This part works by adding itself as the event listener on an element, then
* delegating to the value passed to it. This reduces the number of calls to
* add/removeEventListener if the listener changes frequently, such as when an
* inline function is used as a listener.
*
* Because event options are passed when adding listeners, we must take case
* to add and remove the part as a listener when the event options change.
*/
export class EventPart extends AttributePart {
constructor(...args) {
var _a;
super(...args);
class EventPartImpl extends AttributePartImpl {
constructor() {
super(...arguments);
this.type = EVENT_PART;
this.__eventContext = (_a = args[3]) === null || _a === void 0 ? void 0 : _a.eventContext;
}
_setValue(newListener) {
newListener !== null && newListener !== void 0 ? newListener : (newListener = nothing);
const oldListener = this._value;
// EventPart does not use the base _$setValue/_resolveValue implementation
// since the dirty checking is more complex
/** @internal */
_$setValue(newListener, directiveParent = this) {
newListener =
resolveDirective(this, newListener, directiveParent, 0) ?? nothing;
if (newListener === noChange) {
return;
}
const oldListener = this._$committedValue;
// If the new value is nothing or any options change we have to remove the

@@ -826,20 +976,71 @@ // part as a listener.

}
this._value = newListener;
this._$committedValue = newListener;
}
handleEvent(event) {
var _a;
if (typeof this._value === 'function') {
// TODO (justinfagnani): do we need to default to this.__element?
if (typeof this._$committedValue === 'function') {
// TODO (justinfagnani): do we need to default to this._$element?
// It'll always be the same as `e.currentTarget`.
this._value.call((_a = this.__eventContext) !== null && _a !== void 0 ? _a : this.element, event);
this._$committedValue.call(this.options?.host ?? this.element, event);
}
else {
this._value.handleEvent(event);
this._$committedValue.handleEvent(event);
}
}
}
class ElementPartImpl {
constructor(element, parent, options) {
this.element = element;
this.type = ELEMENT_PART;
/** @internal */
this._$disconnetableChildren = undefined;
/** @internal */
this._setDirectiveConnected = undefined;
this._$parent = parent;
this.options = options;
}
_$setValue(value) {
resolveDirective(this, value);
}
}
/**
* END USERS SHOULD NOT RELY ON THIS OBJECT.
*
* Private exports for use by other Lit packages, not intended for use by
* external users.
*
* We currently do not make a mangled rollup build of the lit-ssr code. In order
* to keep a number of (otherwise private) top-level exports mangled in the
* client side code, we export a _$private object containing those members (or
* helper methods for accessing private fields of those members), and then
* re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
* client-side code is being used in `dev` mode or `prod` mode.
*
* @private
*/
export const _$private = {
// Used in lit-ssr
_boundAttributeSuffix: boundAttributeSuffix,
_marker: marker,
_markerMatch: markerMatch,
_HTML_RESULT: HTML_RESULT,
_getTemplateHtml: getTemplateHtml,
// Used in hydrate
_TemplateInstance: TemplateInstance,
_isIterable: isIterable,
_resolveDirective: resolveDirective,
// Used in tests and private-ssr-support
_ChildPart: ChildPartImpl,
_AttributePart: AttributePartImpl,
_BooleanAttributePart: BooleanAttributePartImpl,
_EventPart: EventPartImpl,
_PropertyPart: PropertyPartImpl,
};
// Apply polyfills if available
// eslint-disable-next-line @typescript-eslint/no-explicit-any
globalThis['litHtmlPlatformSupport']?.(TemplateImpl, ChildPartImpl);
// IMPORTANT: do not change the property name or the assignment expression.
// This line will be used in regexes to search for lit-html usage.
// TODO(justinfagnani): inject version number at build time
((_a = (_b = globalThis)['litHtmlVersions']) !== null && _a !== void 0 ? _a : (_b['litHtmlVersions'] = [])).push('2.0.0-pre.3');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
((_a = globalThis)['litHtmlVersions'] ?? (_a['litHtmlVersions'] = [])).push('2.0.0-pre.4');
//# sourceMappingURL=lit-html.js.map

@@ -14,4 +14,4 @@ /**

*/
import { TemplateResult, NodePart } from '../lit-html.js';
import { NodePartState } from '../parts.js';
import { TemplateResult, ChildPart } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
/**

@@ -31,13 +31,139 @@ * Enables fast switching between multiple templates by caching the DOM nodes

*/
export declare const cache: (v: unknown) => {
_$litDirective$: {
new (): {
templateCache: WeakMap<TemplateStringsArray, NodePartState>;
value?: TemplateResult | undefined;
render(v: unknown): unknown;
update(part: NodePart, [v]: [v: unknown]): unknown;
export declare const cache: (v: unknown) => import("../directive.js").DirectiveResult<{
new (partInfo: PartInfo): {
templateCache: WeakMap<TemplateStringsArray, {
readonly type: 2;
readonly options: import("../lit-html.js").RenderOptions | undefined;
_$committedValue: unknown;
__directive?: Directive | undefined;
_$startNode: ChildNode;
_$endNode: ChildNode | null;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setChildPartConnected?: ((isConnected: boolean, removeFromParent?: boolean | undefined, from?: number | undefined) => void) | undefined;
setConnected: (isConnected: boolean) => void;
readonly parentNode: Node;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent) => void;
_$getTemplate: (strings: TemplateStringsArray, result: TemplateResult) => {
_$element: HTMLTemplateElement;
_parts: ({
readonly _type: 2;
readonly _index: number;
} | {
readonly _type: 1;
readonly _index: number;
readonly _name: string;
readonly _constructor: new (element: HTMLElement, name: string, strings: readonly string[], parent: import("../lit-html.js").Disconnectable | undefined, options: import("../lit-html.js").RenderOptions | undefined) => {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
};
readonly _strings: readonly string[];
} | {
readonly _type: 6;
readonly _index: number;
} | {
readonly _type: 7;
readonly _index: number;
})[];
_$options?: import("../lit-html.js").RenderOptions | undefined;
_$createElement: (html: string) => HTMLTemplateElement;
};
_$clear: (start?: ChildNode | null, from?: number | undefined) => void;
}>;
value?: TemplateResult | undefined;
render(v: unknown): unknown[];
update(containerPart: ChildPart, [v]: [v: unknown]): unknown[];
__part: {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
} | {
readonly type: 2;
readonly options: import("../lit-html.js").RenderOptions | undefined;
_$committedValue: unknown;
__directive?: Directive | undefined;
_$startNode: ChildNode;
_$endNode: ChildNode | null;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setChildPartConnected?: ((isConnected: boolean, removeFromParent?: boolean | undefined, from?: number | undefined) => void) | undefined;
setConnected: (isConnected: boolean) => void;
readonly parentNode: Node;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent) => void;
_$getTemplate: (strings: TemplateStringsArray, result: TemplateResult) => {
_$element: HTMLTemplateElement;
_parts: ({
readonly _type: 2;
readonly _index: number;
} | {
readonly _type: 1;
readonly _index: number;
readonly _name: string;
readonly _constructor: new (element: HTMLElement, name: string, strings: readonly string[], parent: import("../lit-html.js").Disconnectable | undefined, options: import("../lit-html.js").RenderOptions | undefined) => {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: import("../lit-html.js").RenderOptions | undefined;
readonly strings?: readonly string[] | undefined;
_$committedValue: unknown;
__directives?: (Directive | undefined)[] | undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
readonly tagName: string;
_$setValue: (value: unknown, directiveParent?: import("../lit-html.js").DirectiveParent, valueIndex?: number | undefined, noCommit?: boolean | undefined) => void;
_commitValue: (value: unknown) => void;
};
readonly _strings: readonly string[];
} | {
readonly _type: 6;
readonly _index: number;
} | {
readonly _type: 7;
readonly _index: number;
})[];
_$options?: import("../lit-html.js").RenderOptions | undefined;
_$createElement: (html: string) => HTMLTemplateElement;
};
_$clear: (start?: ChildNode | null, from?: number | undefined) => void;
} | {
readonly type: 6;
__directive?: Directive | undefined;
_$committedValue: undefined;
_$parent: import("../lit-html.js").Disconnectable | undefined;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_setDirectiveConnected?: ((directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean | undefined) => void) | undefined;
options: import("../lit-html.js").RenderOptions | undefined;
element: Element;
_$setValue: (value: unknown) => void;
};
__attributeIndex: number | undefined;
__directive?: Directive | undefined;
_$parent: import("../lit-html.js").Disconnectable;
_$disconnetableChildren?: Set<import("../lit-html.js").Disconnectable> | undefined;
_$setDirectiveConnected?(isConnected: boolean): void;
_resolve(props: unknown[]): unknown;
};
values: [v: unknown];
};
}>;
//# sourceMappingURL=cache.d.ts.map

@@ -1,2 +0,2 @@

import{directive as t,Directive as s}from"../lit-html.js";import{detachNodePart as i,restoreNodePart as r}from"../parts.js";
import{render as t,nothing as i}from"../lit-html.js";import{directive as s,Directive as e}from"../directive.js";import{isTemplateResult as o,getComittedValue as r,setComittedValue as h,insertPart as n,clearPart as c}from"../directive-helpers.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/const e=t(class extends s{constructor(){super(...arguments),this.templateCache=new WeakMap}render(t){return t}update(t,[s]){if(void 0!==this.value&&this.value.strings!==s.strings&&this.templateCache.set(this.value.strings,i(t)),void 0!==s._$litType$){let i=this.templateCache.get(s.strings);void 0!==i&&r(t,i),this.value=s}else this.value=void 0;return this.render(s)}});export{e as cache};
*/const d=s(class extends e{constructor(t){super(t),this.templateCache=new WeakMap}render(t){return[t]}update(s,[e]){if(o(this.value)&&(!o(e)||this.value.strings!==e.strings)){const e=r(s).pop();let o=this.templateCache.get(this.value.strings);if(void 0===o){const s=document.createDocumentFragment();o=t(i,s),this.templateCache.set(this.value.strings,o)}h(o,[e]),n(o,void 0,e)}if(!o(e)||o(this.value)&&this.value.strings===e.strings)this.value=void 0;else{const t=this.templateCache.get(e.strings);if(void 0!==t){const i=r(t).pop();c(s),n(s,void 0,i),h(s,[i])}this.value=e}return this.render(e)}});export{d as cache};
//# sourceMappingURL=cache.js.map

@@ -14,3 +14,4 @@ /**

*/
import { AttributePart, Directive, PartInfo } from '../lit-html.js';
import { AttributePart, noChange } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
/**

@@ -28,5 +29,5 @@ * A key-value set of class names to truthy values.

previousClasses?: Set<string>;
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(classInfo: ClassInfo): string;
update(part: AttributePart, [classInfo]: [ClassInfo]): {};
update(part: AttributePart, [classInfo]: DirectiveParameters<this>): string | typeof noChange;
}

@@ -45,9 +46,6 @@ /**

*
* @param classInfo {ClassInfo}
* @param classInfo
*/
export declare const classMap: (classInfo: ClassInfo) => {
_$litDirective$: typeof ClassMap;
values: [classInfo: ClassInfo];
};
export declare const classMap: (classInfo: ClassInfo) => import("../directive.js").DirectiveResult<typeof ClassMap>;
export {};
//# sourceMappingURL=class-map.d.ts.map

@@ -1,2 +0,2 @@

import{directive as t,Directive as s,ATTRIBUTE_PART as e,noChange as i}from"../lit-html.js";
import{noChange as t}from"../lit-html.js";import{directive as s,Directive as r,PartType as e}from"../directive.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/const r=t(class extends s{constructor(t){if(super(),t.type!==e||"class"!==t.name||void 0!==t.strings&&t.strings.length>2)throw Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).filter(s=>t[s]).join(" ")}update(t,[s]){if(void 0===this.previousClasses){this.previousClasses=new Set;for(const t in s)s[t]&&this.previousClasses.add(t);return this.render(s)}const e=t.element.classList;this.previousClasses.forEach(t=>{t in s||(e.remove(t),this.previousClasses.delete(t))});for(const t in s){const i=!!s[t];i!==this.previousClasses.has(t)&&(i?(e.add(t),this.previousClasses.add(t)):(e.remove(t),this.previousClasses.delete(t)))}return i}});export{r as classMap};
*/const i=s(class extends r{constructor(t){if(super(t),t.type!==e.ATTRIBUTE||"class"!==t.name||t.strings?.length>2)throw Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).filter((s=>t[s])).join(" ")}update(s,[r]){if(void 0===this.previousClasses){this.previousClasses=new Set;for(const t in r)r[t]&&this.previousClasses.add(t);return this.render(r)}const e=s.element.classList;this.previousClasses.forEach((t=>{t in r||(e.remove(t),this.previousClasses.delete(t))}));for(const t in r){const s=!!r[t];s!==this.previousClasses.has(t)&&(s?(e.add(t),this.previousClasses.add(t)):(e.remove(t),this.previousClasses.delete(t)))}return t}});export{i as classMap};
//# sourceMappingURL=class-map.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, Part } from '../lit-html.js';
import { Part } from '../lit-html.js';
import { Directive, DirectiveParameters } from '../directive.js';
declare class Guard extends Directive {
previousValue: unknown;
render(_value: unknown, f: () => unknown): unknown;
update(_part: Part, [value, f]: Parameters<this['render']>): unknown;
update(_part: Part, [value, f]: DirectiveParameters<this>): unknown;
}

@@ -60,7 +61,4 @@ /**

*/
export declare const guard: (_value: unknown, f: () => unknown) => {
_$litDirective$: typeof Guard;
values: [_value: unknown, f: () => unknown];
};
export declare const guard: (_value: unknown, f: () => unknown) => import("../directive.js").DirectiveResult<typeof Guard>;
export {};
//# sourceMappingURL=guard.d.ts.map

@@ -1,2 +0,2 @@

import{directive as r,Directive as t,noChange as s}from"../lit-html.js";
import{noChange as r}from"../lit-html.js";import{directive as t,Directive as s}from"../directive.js";
/**

@@ -15,3 +15,3 @@ * @license

*/
const e={},i=r(class extends t{constructor(){super(...arguments),this.previousValue=e}render(r,t){return t()}update(r,[t,e]){if(Array.isArray(t)){if(Array.isArray(this.previousValue)&&this.previousValue.length===t.length&&t.every((r,t)=>r===this.previousValue[t]))return s}else if(this.previousValue===t)return s;return this.previousValue=Array.isArray(t)?Array.from(t):t,this.render(t,e)}});export{i as guard};
const e={},i=t(class extends s{constructor(){super(...arguments),this.previousValue=e}render(r,t){return t()}update(t,[s,e]){if(Array.isArray(s)){if(Array.isArray(this.previousValue)&&this.previousValue.length===s.length&&s.every(((r,t)=>r===this.previousValue[t])))return r}else if(this.previousValue===s)return r;return this.previousValue=Array.isArray(s)?Array.from(s):s,this.render(s,e)}});export{i as guard};
//# sourceMappingURL=guard.js.map

@@ -14,3 +14,3 @@ import{nothing as t}from"../lit-html.js";

* http://polymer.github.io/PATENTS.txt
*/const l=l=>null!=l?l:t;export{l as ifDefined};
*/const o=o=>o??t;export{o as ifDefined};
//# sourceMappingURL=if-defined.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, AttributePart, PartInfo } from '../lit-html.js';
import { AttributePart } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
declare class LiveDirective extends Directive {
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(value: unknown): unknown;
update(part: AttributePart, [value]: Parameters<this['render']>): unknown;
update(part: AttributePart, [value]: DirectiveParameters<this>): unknown;
}

@@ -32,4 +33,4 @@ /**

* bindings hasn't, lit-html won't know to update the DOM value and will leave
* it alone. If this is not what you want—if you want to overwrite the DOM
* value with the bound value no matter what—use the `live()` directive:
* it alone. If this is not what you want--if you want to overwrite the DOM
* value with the bound value no matter what--use the `live()` directive:
*

@@ -44,7 +45,4 @@ * html`<input .value=${live(x)}>`

*/
export declare const live: (value: unknown) => {
_$litDirective$: typeof LiveDirective;
values: [value: unknown];
};
export declare const live: (value: unknown) => import("../directive.js").DirectiveResult<typeof LiveDirective>;
export {};
//# sourceMappingURL=live.d.ts.map

@@ -1,2 +0,2 @@

import{directive as r,Directive as e,EVENT_PART as n,NODE_PART as t,noChange as i,PROPERTY_PART as o,BOOLEAN_ATTRIBUTE_PART as s,nothing as l,ATTRIBUTE_PART as f}from"../lit-html.js";
import{noChange as r,nothing as e}from"../lit-html.js";import{directive as i,Directive as t,PartType as n}from"../directive.js";import{isSingleExpression as o,setComittedValue as s}from"../directive-helpers.js";
/**

@@ -14,4 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/
const u={},c=r(class extends e{constructor(r){if(super(),r.type===n||r.type===t)throw Error("The `live` directive is not allowed on text or event bindings");if(void 0!==r.strings)throw Error("`live` bindings can only contain a single expression")}render(r){return r}update(r,[e]){if(e===i)return e;const n=r.element,t=r.name;if(r.type===o){if(e===n[t])return i}else if(r.type===s){if((e!==l&&!!e)===n.hasAttribute(t))return i}else if(r.type===f&&n.getAttribute(t)===e+"")return i;return r._value=u,e}});export{c as live};
*/const l=i(class extends t{constructor(r){if(super(r),r.type!==n.PROPERTY&&r.type!==n.ATTRIBUTE&&r.type!==n.BOOLEAN_ATTRIBUTE)throw Error("The `live` directive is not allowed on child or event bindings");if(!o(r))throw Error("`live` bindings can only contain a single expression")}render(r){return r}update(i,[t]){if(t===r||t===e)return t;const o=i.element,l=i.name;if(i.type===n.PROPERTY){if(t===o[l])return r}else if(i.type===n.BOOLEAN_ATTRIBUTE){if(!!t===o.hasAttribute(l))return r}else if(i.type===n.ATTRIBUTE&&o.getAttribute(l)===t+"")return r;return s(i),t}});export{l as live};
//# sourceMappingURL=live.js.map

@@ -1,2 +0,2 @@

import{directive as e,Directive as s,NodePart as t,noChange as l}from"../lit-html.js";import{getPartValue as n,setPartValue as o,insertPartBefore as r,removePart as i,createAndInsertPart as f}from"../parts.js";
import{noChange as e}from"../lit-html.js";import{directive as s,Directive as t,PartType as r}from"../directive.js";import{getComittedValue as l,setChildPartValue as i,insertPart as o,removePart as n,setComittedValue as f}from"../directive-helpers.js";
/**

@@ -15,3 +15,3 @@ * @license

*/
const u=(e,s,t)=>{const l=new Map;for(let n=s;n<=t;n++)l.set(e[n],n);return l},c=e(class extends s{constructor(e){if(super(),!(e instanceof t))throw Error("repeat can only be used in text bindings")}_getValuesAndKeys(e,s,t){let l;void 0===t?t=s:void 0!==s&&(l=s);const n=[],o=[];let r=0;for(const s of e)n[r]=l?l(s,r):r,o[r]=t(s,r),r++;return{values:o,keys:n}}render(e,s,t){return this._getValuesAndKeys(e,s,t).values}update(e,[s,t,c]){var a;let d=n(e);const{values:h,keys:p}=this._getValuesAndKeys(s,t,c);if(!d)return this.itemKeys=p,h;const v=null!==(a=this.itemKeys)&&void 0!==a?a:this.itemKeys=[],m=[];let y,x,b=0,g=d.length-1,j=0,k=h.length-1;for(;b<=g&&j<=k;)if(null===d[b])b++;else if(null===d[g])g--;else if(v[b]===p[j])m[j]=o(d[b],h[j]),b++,j++;else if(v[g]===p[k])m[k]=o(d[g],h[k]),g--,k--;else if(v[b]===p[k])m[k]=o(d[b],h[k]),r(e,d[b],m[k+1]),b++,k--;else if(v[g]===p[j])m[j]=o(d[g],h[j]),r(e,d[g],d[b]),g--,j++;else if(void 0===y&&(y=u(p,j,k),x=u(v,b,g)),y.has(v[b]))if(y.has(v[g])){const s=x.get(p[j]),t=void 0!==s?d[s]:null;if(null===t){const s=f(e,d[b]);o(s,h[j]),m[j]=s}else m[j]=o(t,h[j]),r(e,t,d[b]),d[s]=null;j++}else i(d[g]),g--;else i(d[b]),b++;for(;j<=k;){const s=f(e,m[k+1]);o(s,h[j]),m[j++]=s}for(;b<=g;){const e=d[b++];null!==e&&i(e)}return this.itemKeys=p,e._value=m,l}});export{c as repeat};
const u=(e,s,t)=>{const r=new Map;for(let l=s;l<=t;l++)r.set(e[l],l);return r},c=s(class extends t{constructor(e){if(super(e),e.type!==r.CHILD)throw Error("repeat can only be used in text bindings")}At(e,s,t){let r;void 0===t?t=s:void 0!==s&&(r=s);const l=[],i=[];let o=0;for(const s of e)l[o]=r?r(s,o):o,i[o]=t(s,o),o++;return{values:i,keys:l}}render(e,s,t){return this.At(e,s,t).values}update(s,[t,r,c]){const d=l(s),{values:h,keys:p}=this.At(t,r,c);if(!d)return this.itemKeys=p,h;const a=this.itemKeys??(this.itemKeys=[]),v=[];let m,y,j=0,x=d.length-1,b=0,g=h.length-1;for(;j<=x&&b<=g;)if(null===d[j])j++;else if(null===d[x])x--;else if(a[j]===p[b])v[b]=i(d[j],h[b]),j++,b++;else if(a[x]===p[g])v[g]=i(d[x],h[g]),x--,g--;else if(a[j]===p[g])v[g]=i(d[j],h[g]),o(s,v[g+1],d[j]),j++,g--;else if(a[x]===p[b])v[b]=i(d[x],h[b]),o(s,d[j],d[x]),x--,b++;else if(void 0===m&&(m=u(p,b,g),y=u(a,j,x)),m.has(a[j]))if(m.has(a[x])){const e=y.get(p[b]),t=void 0!==e?d[e]:null;if(null===t){const e=o(s,d[j]);i(e,h[b]),v[b]=e}else v[b]=i(t,h[b]),o(s,d[j],t),d[e]=null;b++}else n(d[x]),x--;else n(d[j]),j++;for(;b<=g;){const e=o(s,v[g+1]);i(e,h[b]),v[b++]=e}for(;j<=x;){const e=d[j++];null!==e&&n(e)}return this.itemKeys=p,f(s,v),e}});export{c as repeat};
//# sourceMappingURL=repeat.js.map

@@ -14,3 +14,4 @@ /**

*/
import { AttributePart, Directive, PartInfo } from '../lit-html.js';
import { AttributePart, noChange } from '../lit-html.js';
import { Directive, DirectiveParameters, PartInfo } from '../directive.js';
/**

@@ -28,5 +29,5 @@ * A key-value set of CSS properties and values.

previousStyleProperties?: Set<string>;
constructor(part: PartInfo);
constructor(partInfo: PartInfo);
render(styleInfo: StyleInfo): string;
update(part: AttributePart, [styleInfo]: Parameters<this['render']>): {};
update(part: AttributePart, [styleInfo]: DirectiveParameters<this>): string | typeof noChange;
}

@@ -48,9 +49,6 @@ /**

*
* @param styleInfo {StyleInfo}
* @param styleInfo
*/
export declare const styleMap: (styleInfo: StyleInfo) => {
_$litDirective$: typeof StyleMap;
values: [styleInfo: StyleInfo];
};
export declare const styleMap: (styleInfo: StyleInfo) => import("../directive.js").DirectiveResult<typeof StyleMap>;
export {};
//# sourceMappingURL=style-map.d.ts.map

@@ -1,2 +0,2 @@

import{directive as t,Directive as e,ATTRIBUTE_PART as r,noChange as s}from"../lit-html.js";
import{noChange as t}from"../lit-html.js";import{directive as e,Directive as r,PartType as s}from"../directive.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/const i=t(class extends e{constructor(t){if(super(),t.type!==r||"style"!==t.name||void 0!==t.strings&&t.strings.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce((e,r)=>{const s=t[r];return null===s?e:e+`${r=r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`},"")}update(t,[e]){const{style:r}=t.element;if(void 0===this.previousStyleProperties){this.previousStyleProperties=new Set;for(const t in e)this.previousStyleProperties.add(t);return this.render(e)}this.previousStyleProperties.forEach(t=>{t in e||(this.previousStyleProperties.delete(t),-1===t.indexOf("-")?r[t]=null:r.removeProperty(t))});for(const t in e)this.previousStyleProperties.add(t),-1===t.indexOf("-")?r[t]=e[t]:r.setProperty(t,e[t]);return s}});export{i as styleMap};
*/const i=e(class extends r{constructor(t){if(super(t),t.type!==s.ATTRIBUTE||"style"!==t.name||t.strings?.length>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,r)=>{const s=t[r];return null===s?e:e+`${r=r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`}),"")}update(e,[r]){const{style:s}=e.element;if(void 0===this.previousStyleProperties){this.previousStyleProperties=new Set;for(const t in r)this.previousStyleProperties.add(t);return this.render(r)}this.previousStyleProperties.forEach((t=>{t in r||(this.previousStyleProperties.delete(t),-1===t.indexOf("-")?s[t]=null:s.removeProperty(t))}));for(const t in r)this.previousStyleProperties.add(t),-1===t.indexOf("-")?s[t]=r[t]:s.setProperty(t,r[t]);return t}});export{i as styleMap};
//# sourceMappingURL=style-map.js.map

@@ -14,7 +14,8 @@ /**

*/
import { Directive, PartInfo } from '../lit-html.js';
import { noChange } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
declare class TemplateContent extends Directive {
private __previousTemplate?;
constructor(part: PartInfo);
render(template: HTMLTemplateElement): {};
private _previousTemplate?;
constructor(partInfo: PartInfo);
render(template: HTMLTemplateElement): DocumentFragment | typeof noChange;
}

@@ -28,7 +29,4 @@ /**

*/
export declare const templateContent: (template: HTMLTemplateElement) => {
_$litDirective$: typeof TemplateContent;
values: [template: HTMLTemplateElement];
};
export declare const templateContent: (template: HTMLTemplateElement) => import("../directive.js").DirectiveResult<typeof TemplateContent>;
export {};
//# sourceMappingURL=template-content.d.ts.map

@@ -1,2 +0,2 @@

import{directive as t,Directive as e,NODE_PART as r,noChange as n}from"../lit-html.js";
import{noChange as t}from"../lit-html.js";import{directive as r,Directive as e,PartType as n}from"../directive.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/const o=t(class extends e{constructor(t){if(super(),t.type!==r)throw Error("templateContent can only be used in text bindings")}render(t){return this.t===t?n:(this.t=t,document.importNode(t.content,!0))}});export{o as templateContent};
*/const o=r(class extends e{constructor(t){if(super(t),t.type!==n.CHILD)throw Error("templateContent can only be used in child bindings")}render(r){return this.Mt===r?t:(this.Mt=r,document.importNode(r.content,!0))}});export{o as templateContent};
//# sourceMappingURL=template-content.js.map

@@ -14,3 +14,4 @@ /**

*/
import { Directive, TemplateResult, PartInfo } from '../lit-html.js';
import { nothing, TemplateResult, noChange } from '../lit-html.js';
import { Directive, PartInfo } from '../directive.js';
export declare class UnsafeHTML extends Directive {

@@ -21,4 +22,4 @@ static directiveName: string;

templateResult?: TemplateResult;
constructor(part: PartInfo);
render(value: string): string | TemplateResult | undefined;
constructor(partInfo: PartInfo);
render(value: string | typeof nothing | typeof noChange): TemplateResult | typeof noChange | typeof nothing | undefined;
}

@@ -32,6 +33,3 @@ /**

*/
export declare const unsafeHTML: (value: string) => {
_$litDirective$: typeof UnsafeHTML;
values: [value: string];
};
export declare const unsafeHTML: (value: string | typeof noChange | typeof nothing) => import("../directive.js").DirectiveResult<typeof UnsafeHTML>;
//# sourceMappingURL=unsafe-html.d.ts.map

@@ -1,2 +0,2 @@

import{Directive as t,nothing as r,NODE_PART as i,noChange as s,directive as n}from"../lit-html.js";
import{nothing as t,noChange as i}from"../lit-html.js";import{Directive as r,PartType as s,directive as e}from"../directive.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/class e extends t{constructor(t){if(super(),this.value=r,t.type!==i)throw Error(this.constructor.directiveName+"() can only be used in text bindings")}render(t){if(t===r)return this.templateResult=void 0,this.value=t;if(t===s)return t;if("string"!=typeof t)throw Error(this.constructor.directiveName+"() called with a non-string value");if(t===this.value)return this.templateResult;this.value=t;const i=[t];return i.raw=i,this.templateResult={_$litType$:this.constructor.resultType,strings:i,values:[]}}}e.directiveName="unsafeHTML",e.resultType=1;const o=n(e);export{e as UnsafeHTML,o as unsafeHTML};
*/class n extends r{constructor(i){if(super(i),this.value=t,i.type!==s.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(r){if(r===t)return this.templateResult=void 0,this.value=r;if(r===i)return r;if("string"!=typeof r)throw Error(this.constructor.directiveName+"() called with a non-string value");if(r===this.value)return this.templateResult;this.value=r;const s=[r];return s.raw=s,this.templateResult={_$litType$:this.constructor.resultType,strings:s,values:[]}}}n.directiveName="unsafeHTML",n.resultType=1;const o=e(n);export{n as UnsafeHTML,o as unsafeHTML};
//# sourceMappingURL=unsafe-html.js.map

@@ -26,7 +26,4 @@ /**

*/
export declare const unsafeSVG: (value: string) => {
_$litDirective$: typeof UnsafeSVG;
values: [value: string];
};
export declare const unsafeSVG: (value: string | typeof import("../lit-html.js").noChange | typeof import("../lit-html.js").nothing) => import("../directive.js").DirectiveResult<typeof UnsafeSVG>;
export {};
//# sourceMappingURL=unsafe-svg.d.ts.map

@@ -1,2 +0,2 @@

import{directive as s}from"../lit-html.js";import{UnsafeHTML as t}from"./unsafe-html.js";
import{directive as s}from"../directive.js";import{UnsafeHTML as e}from"./unsafe-html.js";
/**

@@ -14,3 +14,3 @@ * @license

* http://polymer.github.io/PATENTS.txt
*/class m extends t{}m.directiveName="unsafeSVG",m.resultType=2;const o=s(m);export{o as unsafeSVG};
*/class t extends e{}t.directiveName="unsafeSVG",t.resultType=2;const o=s(t);export{o as unsafeSVG};
//# sourceMappingURL=unsafe-svg.js.map

@@ -14,12 +14,45 @@ /**

*/
import { Directive } from './directive.js';
/**
* Used to sanitize any value before it is written into the DOM. This can be
* used to implement a security policy of allowed and disallowed values in
* order to prevent XSS attacks.
*
* One way of using this callback would be to check attributes and properties
* against a list of high risk fields, and require that values written to such
* fields be instances of a class which is safe by construction. Closure's Safe
* HTML Types is one implementation of this technique (
* https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md).
* The TrustedTypes polyfill in API-only mode could also be used as a basis
* for this technique (https://github.com/WICG/trusted-types).
*
* @param node The HTML node (usually either a #text node or an Element) that
* is being written to. Note that this is just an exemplar node, the write
* may take place against another instance of the same class of node.
* @param name The name of an attribute or property (for example, 'href').
* @param type Indicates whether the write that's about to be performed will
* be to a property or a node.
* @return A function that will sanitize this class of writes.
*/
export declare type SanitizerFactory = (node: Node, name: string, type: 'property' | 'attribute') => ValueSanitizer;
/**
* A function which can sanitize values that will be written to a specific kind
* of DOM sink.
*
* See SanitizerFactory.
*
* @param value The value to sanitize. Will be the actual value passed into
* the lit-html template literal, so this could be of any type.
* @return The value to write to the DOM. Usually the same as the input value,
* unless sanitization is needed.
*/
export declare type ValueSanitizer = (value: unknown) => unknown;
/** TemplateResult types */
declare const HTML_RESULT = 1;
declare const SVG_RESULT = 2;
/** TemplatePart types */
export declare const ATTRIBUTE_PART = 1;
export declare const NODE_PART = 2;
export declare const PROPERTY_PART = 3;
export declare const BOOLEAN_ATTRIBUTE_PART = 4;
export declare const EVENT_PART = 5;
declare type ResultType = typeof HTML_RESULT | typeof SVG_RESULT;
declare const ATTRIBUTE_PART = 1;
declare const CHILD_PART = 2;
declare const ELEMENT_PART = 6;
declare const COMMENT_PART = 7;
/**

@@ -47,47 +80,7 @@ * The return type of the template tag functions.

*/
export declare const noChange: {};
export declare const noChange: unique symbol;
/**
* A sentinel value that signals a NodePart to fully clear its content.
* A sentinel value that signals a ChildPart to fully clear its content.
*/
export declare const nothing: {};
export declare type NodePartInfo = {
readonly type: typeof NODE_PART;
};
export declare type AttributePartInfo = {
readonly type: typeof ATTRIBUTE_PART | typeof PROPERTY_PART | typeof BOOLEAN_ATTRIBUTE_PART | typeof EVENT_PART;
strings?: ReadonlyArray<string>;
name: string;
tagName: string;
};
/**
* Information about the part a directive is bound to.
*
* This is useful for checking that a directive is attached to a valid part,
* such as with directive that can only be used on attribute bindings.
*/
export declare type PartInfo = NodePartInfo | AttributePartInfo;
export declare type DirectiveClass = {
new (part: PartInfo): Directive;
};
/**
* This utility type extracts the signature of a directive class's render()
* method so we can use it for the type of the generated directive function.
*/
export declare type DirectiveParameters<C extends DirectiveClass> = Parameters<InstanceType<C>['render']>;
/**
* A generated directive function doesn't evaluate the directive, but just
* returns a DirectiveResult object that captures the arguments.
*/
declare type DirectiveResult<C extends DirectiveClass = DirectiveClass> = {
_$litDirective$: C;
values: DirectiveParameters<C>;
};
/**
* Creates a user-facing directive function from a Directive class. This
* function has the same parameters as the directive's render() method.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
*/
export declare const directive: <C extends DirectiveClass>(c: C) => (...values: Parameters<InstanceType<C>["render"]>) => DirectiveResult<C>;
export declare const nothing: unique symbol;
export interface RenderOptions {

@@ -98,7 +91,7 @@ /**

*/
readonly eventContext?: EventTarget;
host?: EventTarget;
/**
* A DOM node before which to render content in the container.
*/
readonly renderBefore?: ChildNode | null;
renderBefore?: ChildNode | null;
}

@@ -111,37 +104,133 @@ /**

*/
export declare const render: (value: unknown, container: HTMLElement | DocumentFragment, options?: RenderOptions | undefined) => void;
export declare const render: {
(value: unknown, container: HTMLElement | DocumentFragment, options?: RenderOptions | undefined): ChildPart;
setSanitizer: (newSanitizer: SanitizerFactory) => void;
createSanitizer: SanitizerFactory;
_testOnlyClearSanitizerFactoryDoNotCallOrElse: () => void;
};
export interface DirectiveParent {
_$parent?: DirectiveParent;
__directive?: Directive;
__directives?: Array<Directive | undefined>;
}
export declare type Template = Interface<TemplateImpl>;
declare class TemplateImpl {
/** @internal */
_$element: HTMLTemplateElement;
/** @internal */
_parts: Array<TemplatePart>;
_$options?: RenderOptions;
constructor({ strings, _$litType$: type }: TemplateResult, options?: RenderOptions);
_$createElement(html: string): HTMLTemplateElement;
}
export interface Disconnectable {
_$parent?: Disconnectable;
_$disconnetableChildren?: Set<Disconnectable>;
}
declare function resolveDirective(part: ChildPart | AttributePart | ElementPart, value: unknown, _$parent?: DirectiveParent, _$attributeIndex?: number): unknown;
/**
* Base class for creating custom directives. Users should extend this class,
* implement `render` and/or `update`, and then pass their subclass to
* `directive`.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
* An updateable instance of a Template. Holds references to the Parts used to
* update the template instance.
*/
export declare abstract class Directive {
abstract render(...props: Array<unknown>): unknown;
update(_part: Part, props: Array<unknown>): unknown;
declare class TemplateInstance {
/** @internal */
_$template: Template;
/** @internal */
_parts: Array<Part | undefined>;
/** @internal */
_$parent: Disconnectable;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
constructor(template: Template, parent: ChildPart);
_clone(options: RenderOptions | undefined): DocumentFragment;
_update(values: Array<unknown>): void;
}
export declare type Part = NodePart | AttributePart | PropertyPart | BooleanAttributePart;
export declare class NodePart {
_startNode: ChildNode;
_endNode: ChildNode | null;
options: RenderOptions | undefined;
declare type AttributePartConstructor = {
new (element: HTMLElement, name: string, strings: ReadonlyArray<string>, parent: Disconnectable | undefined, options: RenderOptions | undefined): AttributePart;
};
declare type AttributeTemplatePart = {
readonly _type: typeof ATTRIBUTE_PART;
readonly _index: number;
readonly _name: string;
/** @internal */
readonly _constructor: AttributePartConstructor;
/** @internal */
readonly _strings: ReadonlyArray<string>;
};
declare type NodeTemplatePart = {
readonly _type: typeof CHILD_PART;
readonly _index: number;
};
declare type ElementTemplatePart = {
readonly _type: typeof ELEMENT_PART;
readonly _index: number;
};
declare type CommentTemplatePart = {
readonly _type: typeof COMMENT_PART;
readonly _index: number;
};
/**
* A TemplatePart represents a dynamic part in a template, before the template
* is instantiated. When a template is instantiated Parts are created from
* TemplateParts.
*/
declare type TemplatePart = NodeTemplatePart | AttributeTemplatePart | ElementTemplatePart | CommentTemplatePart;
export declare type Part = ChildPart | AttributePart | PropertyPart | BooleanAttributePart | ElementPart | EventPart;
declare type Interface<T> = {
[P in keyof T]: T[P];
};
export declare type ChildPart = Interface<ChildPartImpl>;
declare class ChildPartImpl {
readonly type = 2;
_value: unknown;
protected __directive?: Directive;
constructor(_startNode: ChildNode, _endNode: ChildNode | null, options: RenderOptions | undefined);
_setValue(value: unknown): void;
private __insert;
private __commitDirective;
readonly options: RenderOptions | undefined;
_$committedValue: unknown;
/** @internal */
__directive?: Directive;
/** @internal */
_$startNode: ChildNode;
/** @internal */
_$endNode: ChildNode | null;
private _textSanitizer;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
/** @internal */
_$setChildPartConnected?(isConnected: boolean, removeFromParent?: boolean, from?: number): void;
constructor(startNode: ChildNode, endNode: ChildNode | null, parent: TemplateInstance | ChildPart | undefined, options: RenderOptions | undefined);
/**
* Sets the connection state for any `DisconnectableDirectives` contained
* within this part and runs their `disconnectedCallback` or
* `reconnectedCallback`, according to the `isConnected` argument.
* @param isConnected
*/
setConnected(isConnected: boolean): void;
get parentNode(): Node;
_$setValue(value: unknown, directiveParent?: DirectiveParent): void;
private _insert;
private _commitNode;
private __commitText;
private __commitTemplateResult;
private __commitIterable;
__clear(start?: ChildNode | null): void;
private _commitText;
private _commitTemplateResult;
/** @internal */
_$getTemplate(strings: TemplateStringsArray, result: TemplateResult): Interface<TemplateImpl>;
private _commitIterable;
/**
* Removes the nodes contained within this Part from the DOM.
*
* @param start Start node to clear from, for clearing a subset of the part's
* DOM (used when truncating iterables)
* @param from When `start` is specified, the index within the iterable from
* which ChildParts are being removed, used for disconnecting directives in
* those Parts.
*
* @internal
*/
_$clear(start?: ChildNode | null, from?: number): void;
}
export declare class AttributePart {
export declare type AttributePart = Interface<AttributePartImpl>;
declare class AttributePartImpl {
readonly type: 1 | 5 | 4 | 3;
readonly element: HTMLElement;
readonly name: string;
readonly options: RenderOptions | undefined;
/**

@@ -153,24 +242,23 @@ * If this attribute part represents an interpolation, this contains the

readonly strings?: ReadonlyArray<string>;
_value: unknown | Array<unknown>;
private __directives?;
/** @internal */
_$committedValue: unknown | Array<unknown>;
/** @internal */
__directives?: Array<Directive | undefined>;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
protected _sanitizer: ValueSanitizer | undefined;
/** @internal */
_setDirectiveConnected?: (directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean) => void;
get tagName(): string;
constructor(element: HTMLElement, name: string, strings: ReadonlyArray<string>, _options?: RenderOptions);
constructor(element: HTMLElement, name: string, strings: ReadonlyArray<string>, parent: Disconnectable | undefined, options: RenderOptions | undefined);
/**
* Normalizes a user-provided value before writing it to the DOM. In the
* near future this will include invoking a directive if the value is
* a DirectiveResult.
*
* @param value the raw input value to normalize
* @param _i the index in the values array this value was read from
*/
__resolveValue(value: unknown, i: number): unknown;
/**
* Sets the value of this part.
*
* If this part is single-valued, `this.__strings` will be undefined, and the
* Sets the value of this part by resolving the value from possibly multiple
* values and static strings and committing it to the DOM.
* If this part is single-valued, `this._strings` will be undefined, and the
* method will be called with a single value argument. If this part is
* multi-value, `this.__strings` will be defined, and the method is called
* multi-value, `this._strings` will be defined, and the method is called
* with the value array of the part's owning TemplateInstance, and an offset
* into the value array from which the values should be read.
*
* This method is overloaded this way to eliminate short-lived array slices

@@ -181,20 +269,26 @@ * of the template instance values, and allow a fast-path for single-valued

* @param value The part value, or an array of values for multi-valued parts
* @param from the index to start reading values from. `undefined` for
* @param valueIndex the index to start reading values from. `undefined` for
* single-valued parts
* @param noCommit causes the part to not commit its value to the DOM. Used
* in hydration to prime attribute parts with their first-rendered value,
* but not set the attribute, and in SSR to no-op the DOM operation and
* capture the value for serialization.
*
* @internal
*/
_setValue(value: unknown): void;
_setValue(value: Array<unknown>, from: number): void;
/**
* Writes the value to the DOM. An override point for PropertyPart and
* BooleanAttributePart.
*/
__commitValue(value: unknown): void;
_$setValue(value: unknown | Array<unknown>, directiveParent?: DirectiveParent, valueIndex?: number, noCommit?: boolean): void;
/** @internal */
_commitValue(value: unknown): void;
}
export declare class PropertyPart extends AttributePart {
export declare type PropertyPart = Interface<PropertyPartImpl>;
declare class PropertyPartImpl extends AttributePartImpl {
readonly type = 3;
__commitValue(value: unknown): void;
/** @internal */
_commitValue(value: unknown): void;
}
export declare class BooleanAttributePart extends AttributePart {
export declare type BooleanAttributePart = Interface<BooleanAttributePartImpl>;
declare class BooleanAttributePartImpl extends AttributePartImpl {
readonly type = 4;
__commitValue(value: unknown): void;
/** @internal */
_commitValue(value: unknown): void;
}

@@ -212,10 +306,57 @@ /**

*/
export declare class EventPart extends AttributePart {
export declare type EventPart = Interface<EventPartImpl>;
declare class EventPartImpl extends AttributePartImpl {
readonly type = 5;
__eventContext?: unknown;
constructor(...args: ConstructorParameters<typeof AttributePart>);
_setValue(newListener: unknown): void;
/** @internal */
_$setValue(newListener: unknown, directiveParent?: DirectiveParent): void;
handleEvent(event: Event): void;
}
export declare type ElementPart = Interface<ElementPartImpl>;
declare class ElementPartImpl {
element: Element;
readonly type = 6;
/** @internal */
__directive?: Directive;
_$committedValue: undefined;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable>;
/** @internal */
_setDirectiveConnected?: (directive: Directive | undefined, isConnected: boolean, removeFromParent?: boolean) => void;
options: RenderOptions | undefined;
constructor(element: Element, parent: Disconnectable, options: RenderOptions | undefined);
_$setValue(value: unknown): void;
}
/**
* END USERS SHOULD NOT RELY ON THIS OBJECT.
*
* Private exports for use by other Lit packages, not intended for use by
* external users.
*
* We currently do not make a mangled rollup build of the lit-ssr code. In order
* to keep a number of (otherwise private) top-level exports mangled in the
* client side code, we export a _$private object containing those members (or
* helper methods for accessing private fields of those members), and then
* re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
* client-side code is being used in `dev` mode or `prod` mode.
*
* @private
*/
export declare const _$private: {
_boundAttributeSuffix: string;
_marker: string;
_markerMatch: string;
_HTML_RESULT: number;
_getTemplateHtml: (strings: TemplateStringsArray, type: ResultType) => [string, string[]];
_TemplateInstance: typeof TemplateInstance;
_isIterable: (value: unknown) => value is Iterable<unknown>;
_resolveDirective: typeof resolveDirective;
_ChildPart: typeof ChildPartImpl;
_AttributePart: AttributePartConstructor;
_BooleanAttributePart: AttributePartConstructor;
_EventPart: AttributePartConstructor;
_PropertyPart: AttributePartConstructor;
};
export {};
//# sourceMappingURL=lit-html.d.ts.map

@@ -14,3 +14,3 @@ /**

*/
var t,s;const i=`lit$${(Math.random()+"").slice(9)}$`,e="?"+i,h=`<${e}>`,o=document,l=(t="")=>o.createComment(t),n=t=>null===t||"object"!=typeof t&&"function"!=typeof t,r=Array.isArray,c=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_=/-->/g,u=/>/g,a=/>|[ \n \r]([^--Ÿ "'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))/g,v=/'/g,d=/"/g,f=/^(?:script|style|textarea)$/i,p=1,m=2,g=3,y=4,$=5,x=t=>(s,...i)=>({_$litType$:t,strings:s,values:i}),w=x(1),V=x(2),b={},T={},A=new Map,z=t=>(...s)=>({_$litDirective$:t,values:s}),D=(t,s,i)=>{var e,h;const o=null!==(e=null==i?void 0:i.renderBefore)&&void 0!==e?e:s;let n=o.$lit$;if(void 0===n){const t=null!==(h=null==i?void 0:i.renderBefore)&&void 0!==h?h:null;o.$lit$=n=new Z(s.insertBefore(l(),t),t,i)}n._setValue(t)},E=o.createTreeWalker(o);class M{update(t,s){return this.render(...s)}}class N{constructor({strings:t,_$litType$:s}){this.t=[],E.currentNode=(this.i=o.createElement("template")).content;const n=(this.h=t).length-1,r=[];let p,m,g=2===s?"<svg>":"",y=0,$=0,x=0,w=c;for(let s=0;s<n;s++){const e=t[s];let o,l,n=-1,p=0;for(;p<e.length;){if(w.lastIndex=p,l=w.exec(e),null===l){w===a&&(n=-1);break}p=w.lastIndex,w===c?"!--"===l[1]?w=_:void 0!==l[1]?w=u:void 0!==l[2]?(f.test(l[2])&&(m=RegExp("</"+l[2],"g")),w=a):void 0!==l[3]&&(w=a):w===a?">"===l[0]?(w=null!=m?m:c,n=-1):(n=w.lastIndex-l[2].length,o=l[1],w=void 0===l[3]?a:'"'===l[3]?d:v):w===d||w===v?w=a:w===_||w===u?w=c:(w=a,m=void 0)}g+=w===c?e+h:(-1!==n?(r.push(o),e.slice(0,n)+"$lit$"+e.slice(n)):e)+i}if(this.i.innerHTML=g+this.h[n],2===s){const t=this.i.content,s=t.firstChild;s.remove(),t.append(...s.childNodes)}for(;null!==(p=E.nextNode())&&$<n;){if(1===p.nodeType){if(p.hasAttributes()){const{attributes:t}=p;for(let s=0;s<t.length;s++){const{name:e,value:h}=t[s];if(e.endsWith("$lit$")){s--,p.removeAttribute(e);const t=h.split(i),o=/([.?@])?(.*)/.exec(r[x++]);this.t.push({o:1,l:y,_:o[2],h:t,u:"."===o[1]?k:"?"===o[1]?I:"@"===o[1]?S:j}),$+=t.length-1}else e===i&&(p.removeAttribute(e),s--,this.t.push({o:6,l:y}))}}if(f.test(p.tagName)){const t=p.textContent.split(i),s=t.length-1;if(s>0){p.textContent="";for(let i=0;i<s;i++)p.append(t[i]||l()),this.t.push({o:2,l:++y}),$++;p.append(t[s]||l())}}}else if(8===p.nodeType)if(p.data===e)$++,this.t.push({o:2,l:y});else{let t=-1;for(;-1!==(t=p.data.indexOf(i,t+1));)this.t.push({o:7,l:y}),$++,t+=i.length-1}y++}}}class R{constructor(t){this.t=[],this.v=t}p(t){const{i:{content:s},t:i}=this.v,e=o.importNode(s,!0);E.currentNode=e;let h=E.nextNode(),l=0,n=0,r=i[0];for(;void 0!==r&&null!==h;){if(l===r.l){let s;2===r.o?s=new Z(h,h.nextSibling,t):1===r.o&&(s=new r.u(h,r._,r.h,t)),this.t.push(s),r=i[++n]}void 0!==r&&l!==r.l&&(h=E.nextNode(),l++)}return e}m(t){let s=0;for(const i of this.t)void 0!==i?void 0!==i.strings?(i._setValue(t,s),s+=i.strings.length-1):i._setValue(t[s++]):s++}}class Z{constructor(t,s,i){this._startNode=t,this._endNode=s,this.options=i,this.type=2}_setValue(t){n(t)?t!==this._value&&this.g(t):void 0!==t._$litType$?this.$(t):void 0!==t._$litDirective$?this.V(t):void 0!==t.nodeType?this._commitNode(t):(t=>r(t)||t&&"function"==typeof t[Symbol.iterator])(t)?this.T(t):t===T?(this._value=T,this.A()):t!==b&&this.g(t)}D(t,s=this._endNode){return this._startNode.parentNode.insertBefore(t,s)}V(t){var s;const i=t._$litDirective$;(null===(s=this.M)||void 0===s?void 0:s.constructor)!==i&&(this.A(),this.M=new i(this)),this._setValue(this.M.update(this,t.values))}_commitNode(t){this._value!==t&&(this.A(),this._value=this.D(t))}g(t){const s=this._startNode.nextSibling;null!=t||(t=""),null!==s&&3===s.nodeType&&(null===this._endNode?null===s.nextSibling:s===this._endNode.previousSibling)?s.data=t:this._commitNode(new Text(t)),this._value=t}$(t){const{strings:s,values:i}=t;let e=A.get(s);if(void 0===e&&A.set(s,e=new N(t)),null!=this._value&&this._value.v===e)this._value.m(i);else{const t=new R(e),s=t.p(this.options);t.m(i),this._commitNode(s),this._value=t}}T(t){r(this._value)||(this._value=[],this.A());const s=this._value;let i,e=0;for(const h of t)e===s.length?s.push(i=new Z(this.D(l()),this.D(l()),this.options)):i=s[e],i._setValue(h),e++;e<s.length&&(s.length=e,this.A(null==i?void 0:i._endNode.nextSibling))}A(t=this._startNode.nextSibling){for(;t&&t!==this._endNode;){const s=t.nextSibling;t.remove(),t=s}}}class j{constructor(t,s,i,e){this.type=1,this._value=T,this.element=t,this.name=s,i.length>2||""!==i[0]||""!==i[1]?(this._value=Array(i.length-1).fill(T),this.strings=i):this._value=T}get tagName(){return this.element.tagName}N(t,s){var i,e;const h=null===(i=t)||void 0===i?void 0:i._$litDirective$;if(void 0!==h){let i=(null!==(e=this.R)&&void 0!==e?e:this.R=[])[s];(null==i?void 0:i.constructor)!==h&&(i=this.R[s]=new h(this)),t=i.update(this,t.values)}return null!=t?t:""}_setValue(t,s){const i=this.strings;if(void 0===i){const s=this.N(t,0);(n(s)||s===T)&&s===this._value||s===b||this.Z(this._value=s)}else{let e,h,o=i[0],l=!1,r=!1;for(e=0;e<i.length-1;e++)h=this.N(t[s+e],e),h===b?h=this._value[e]:(r=r||h===T,l=l||!((n(h)||h===T)&&h===this._value[e]),this._value[e]=h),o+=("string"==typeof h?h:h+"")+i[e+1];l&&this.Z(r?T:o)}}Z(t){t===T?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t)}}class k extends j{constructor(){super(...arguments),this.type=3}Z(t){this.element[this.name]=t===T?void 0:t}}class I extends j{constructor(){super(...arguments),this.type=4}Z(t){t&&t!==T?this.element.setAttribute(this.name,""):this.element.removeAttribute(this.name)}}class S extends j{constructor(...t){var s;super(...t),this.type=5,this.j=null===(s=t[3])||void 0===s?void 0:s.eventContext}_setValue(t){null!=t||(t=T);const s=this._value,i=t===T&&s!==T||t.capture!==s.capture||t.once!==s.once||t.passive!==s.passive,e=t!==T&&(s===T||i);i&&this.element.removeEventListener(this.name,this,s),e&&this.element.addEventListener(this.name,this,t),this._value=t}handleEvent(t){var s;"function"==typeof this._value?this._value.call(null!==(s=this.j)&&void 0!==s?s:this.element,t):this._value.handleEvent(t)}}(null!==(t=(s=globalThis).litHtmlVersions)&&void 0!==t?t:s.litHtmlVersions=[]).push("2.0.0-pre.3");export{p as ATTRIBUTE_PART,j as AttributePart,y as BOOLEAN_ATTRIBUTE_PART,I as BooleanAttributePart,M as Directive,$ as EVENT_PART,S as EventPart,m as NODE_PART,Z as NodePart,g as PROPERTY_PART,k as PropertyPart,z as directive,w as html,b as noChange,T as nothing,D as render,V as svg};
var t;const s=`lit$${(Math.random()+"").slice(9)}$`,i="?"+s,e=`<${i}>`,h=document,o=(t="")=>h.createComment(t),n=t=>null===t||"object"!=typeof t&&"function"!=typeof t,r=Array.isArray,l=t=>r(t)||"function"==typeof t?.[Symbol.iterator],c=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,a=/-->/g,u=/>/g,_=/>|[ \n \r](?:([^--Ÿ "'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,d=/'/g,f=/"/g,v=/^(?:script|style|textarea)$/i,m=t=>(s,...i)=>({_$litType$:t,strings:s,values:i}),p=m(1),$=m(2),g=Symbol.for("lit-noChange"),y=Symbol.for("lit-nothing"),x=new Map,b=(t,s,i)=>{const e=i?.renderBefore??s;let h=e.t;if(void 0===h){const t=i?.renderBefore??null;e.t=h=new E(s.insertBefore(o(),t),t,void 0,i)}return h.M(t),h},T=h.createTreeWalker(h,133,null,!1),w=(t,i)=>{const h=t.length-1,o=[];let n,r=2===i?"<svg>":"",l=c;for(let i=0;i<h;i++){const h=t[i];let m,p,$=-1,g=0;for(;g<h.length&&(l.lastIndex=g,p=l.exec(h),null!==p);)g=l.lastIndex,l===c?"!--"===p[1]?l=a:void 0!==p[1]?l=u:void 0!==p[2]?(v.test(p[2])&&(n=RegExp("</"+p[2],"g")),l=_):void 0!==p[3]&&(l=_):l===_?">"===p[0]?(l=n??c,$=-1):void 0===p[1]?$=-2:($=l.lastIndex-p[2].length,m=p[1],l=void 0===p[3]?_:'"'===p[3]?f:d):l===f||l===d?l=_:l===a||l===u?l=c:(l=_,n=void 0);r+=l===c?h+e:$>=0?(o.push(m),h.slice(0,$)+"$lit$"+h.slice($)+s):h+s+(-2===$?":"+i:"")}return[r+(t[h]||"<?>")+(2===i?"</svg>":""),o]};class A{constructor({strings:t,_$litType$:e},h){let n;this.o=[],this.C=h;let r=0,l=0,c=0;const a=t.length-1,[u,_]=w(t,e);if(this.B=this.A(u),T.currentNode=this.B.content,2===e){const t=this.B.content,s=t.firstChild;s.remove(),t.append(...s.childNodes)}for(;null!==(n=T.nextNode())&&l<a;){if(1===n.nodeType){if(n.hasAttributes()){const t=[];for(const i of n.getAttributeNames())if(i.endsWith("$lit$")){const e=_[c++],h=n.getAttribute(e.toLowerCase()+"$lit$");t.push(i);const o=h.split(s),a=/([.?@])?(.*)/.exec(e);this.o.push({h:1,l:r,u:a[2],v:o,p:"."===a[1]?I:"?"===a[1]?M:"@"===a[1]?C:S}),l+=o.length-1}else i.startsWith(s)&&(t.push(i),this.o.push({h:6,l:r}));for(const s of t)n.removeAttribute(s)}if(v.test(n.tagName)){const t=n.textContent.split(s),i=t.length-1;if(i>0){n.textContent="";for(let s=0;s<i;s++)n.append(t[s]||o()),this.o.push({h:2,l:++r}),l++;n.append(t[i]||o())}}}else if(8===n.nodeType)if(n.data===i)l++,this.o.push({h:2,l:r});else{let t=-1;for(;-1!==(t=n.data.indexOf(s,t+1));)this.o.push({h:7,l:r}),l++,t+=s.length-1}r++}}A(t){const s=h.createElement("template");return s.innerHTML=t,s}}function V(t,s,i=t,e){var h;let o=void 0!==e?i.Σ_?.[e]:i.Σm;const r=n(s)?void 0:s._$litDirective$;return o?.constructor!==r&&(o?.R?.(!1),o=void 0===r?void 0:new r({...t,$:t,P:i,g:e}),void 0!==e?((h=i).Σ_??(h.Σ_=[]))[e]=o:i.Σm=o),void 0!==o&&(s=o.k(s.values)),s}class P{constructor(t,s){this.o=[],this.Q=void 0,this.G=t,this.P=s}V(t){const{B:{content:s},o:i}=this.G,e=h.importNode(s,!0);T.currentNode=e;let o=T.nextNode(),n=0,r=0,l=i[0];for(;void 0!==l&&null!==o;){if(n===l.l){let s;2===l.h?s=new E(o,o.nextSibling,this,t):1===l.h?s=new l.p(o,l.u,l.v,this,t):6===l.h&&(s=new N(o,this,t)),this.o.push(s),l=i[++r]}void 0!==l&&n!==l.l&&(o=T.nextNode(),n++)}return e}j(t){let s=0;for(const i of this.o)void 0!==i&&(void 0!==i.strings?(i.M(t,i,s),s+=i.strings.length-2):i.M(t[s])),s++}}class E{constructor(t,s,i,e){this.type=2,this.Q=void 0,this.D=t,this.E=s,this.P=i,this.options=e}setConnected(t){this.S?.(t)}get parentNode(){return this.D.parentNode}M(t,s=this){t=V(this,t,s),n(t)?t===y?(this.T(),this.L=y):t!==this.L&&t!==g&&this.Z(t):void 0!==t._$litType$?this.U(t):void 0!==t.nodeType?this.Y(t):l(t)?this.q(t):this.Z(t)}X(t,s=this.E){return this.D.parentNode.insertBefore(t,s)}Y(t){this.L!==t&&(this.T(),this.L=this.X(t))}Z(t){const s=this.D.nextSibling;t??(t=""),null!==s&&3===s.nodeType&&(null===this.E?null===s.nextSibling:s===this.E.previousSibling)?s.data=t:this.Y(h.createTextNode(t)),this.L=t}U(t){const{values:s,strings:i}=t,e=this.F(i,t);if(this.L?.G===e)this.L.j(s);else{const t=new P(e,this),i=t.V(this.options);t.j(s),this.Y(i),this.L=t}}F(t,s){let i=x.get(t);return void 0===i&&x.set(t,i=new A(s)),i}q(t){r(this.L)||(this.L=[],this.T());const s=this.L;let i,e=0;for(const h of t)e===s.length?s.push(i=new E(this.X(o()),this.X(o()),this,this.options)):i=s[e],i.M(h),e++;e<s.length&&(this.T(i?.E.nextSibling,e),s.length=e)}T(t=this.D.nextSibling,s){for(this.S?.(!1,!0,s);t&&t!==this.E;){const s=t.nextSibling;t.remove(),t=s}}}class S{constructor(t,s,i,e,h){this.type=1,this.L=y,this.Q=void 0,this.tt=void 0,this.element=t,this.name=s,this.P=e,this.options=h,i.length>2||""!==i[0]||""!==i[1]?(this.L=Array(i.length-1).fill(y),this.strings=i):this.L=y}get tagName(){return this.element.tagName}M(t,s=this,i,e){const h=this.strings;let o=!1;if(void 0===h)t=V(this,t,s,0),o=!n(t)||t!==this.L&&t!==g,o&&(this.L=t);else{const e=t;let r,l;for(t=h[0],r=0;r<h.length-1;r++)l=V(this,e[i+r],s,r),l===g&&(l=this.L[r]),o||(o=!n(l)||l!==this.L[r]),l===y?t=y:t!==y&&(t+=(l??"")+h[r+1]),this.L[r]=l}o&&!e&&this.it(t)}it(t){t===y?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"")}}class I extends S{constructor(){super(...arguments),this.type=3}it(t){this.element[this.name]=t===y?void 0:t}}class M extends S{constructor(){super(...arguments),this.type=4}it(t){t&&t!==y?this.element.setAttribute(this.name,""):this.element.removeAttribute(this.name)}}class C extends S{constructor(){super(...arguments),this.type=5}M(t,s=this){if((t=V(this,t,s,0)??y)===g)return;const i=this.L,e=t===y&&i!==y||t.capture!==i.capture||t.once!==i.once||t.passive!==i.passive,h=t!==y&&(i===y||e);e&&this.element.removeEventListener(this.name,this,i),h&&this.element.addEventListener(this.name,this,t),this.L=t}handleEvent(t){"function"==typeof this.L?this.L.call(this.options?.host??this.element,t):this.L.handleEvent(t)}}class N{constructor(t,s,i){this.element=t,this.type=6,this.Q=void 0,this.tt=void 0,this.P=s,this.options=i}M(t){V(this,t)}}const R={et:"$lit$",nt:s,ot:i,rt:1,ht:w,lt:P,ct:l,dt:V,ut:E,at:S,ft:M,vt:C,wt:I};globalThis.litHtmlPlatformSupport?.(A,E),((t=globalThis).litHtmlVersions??(t.litHtmlVersions=[])).push("2.0.0-pre.4");export{R as _$private,p as html,g as noChange,y as nothing,b as render,$ as svg};
//# sourceMappingURL=lit-html.js.map
{
"name": "lit-html",
"version": "2.0.0-pre.3",
"version": "2.0.0-pre.4",
"description": "HTML templates literals in JavaScript",
"license": "BSD-3-Clause",
"repository": "Polymer/lit-html",
"author": "The Polymer Authors",
"homepage": "https://lit-html.polymer-project.org/",
"repository": "Polymer/lit-html",
"main": "lit-html.js",
"type": "module",
"main": "lit-html.js",
"typings": "lit-html.d.ts",
"directories": {
"test": "test"
"exports": {
".": {
"development": "./development/lit-html.js",
"default": "./lit-html.js"
},
"./directive.js": {
"development": "./development/directive.js",
"default": "./directive.js"
},
"./directive-helpers.js": {
"development": "./development/directive-helpers.js",
"default": "./directive-helpers.js"
},
"./disconnectable-directive.js": {
"development": "./development/disconnectable-directive.js",
"default": "./disconnectable-directive.js"
},
"./platform-support.js": {
"development": "./development/platform-support.js",
"default": "./platform-support.js"
},
"./directives/": {
"development": "./development/directives/",
"default": "./directives/"
},
"./hydrate.js": {
"development": "./development/hydrate.js",
"default": "./hydrate.js"
},
"./private-ssr-support.js": {
"development": "./development/private-ssr-support.js",
"default": "./private-ssr-support.js"
}
},
"files": [
"/lit-html.js",
"/lit-html.js.map",
"/lit-html.d.ts",
"/lit-html.d.ts.map",
"/directives/",
"/parts.js",
"/parts.js.map",
"/parts.d.ts",
"/parts.d.ts.map",
"/src/",
"!/src/test/",
"/development/",
"!/development/test/"
],
"scripts": {
"build": "npm run clean && tsc && rollup -c",
"clean": "rm -rf lit-html.{js,js.map,d.ts} directives/ development/",
"build:watch": "rollup -c --watch",
"build:ts": "tsc",
"build:ts:watch": "tsc --watch",
"check-version": "node scripts/check-version-tracker.js",
"checksize": "rollup -c --environment=CHECKSIZE",
"clean": "rm -rf lit-html.{js,js.map,d.ts} platform-support.{js,js.map,d.ts} directive.{js,js.map,d.ts} directive-helpers.{js,js.map,d.ts} disconnectable-directive.{js,js.map,d.ts} hydrate.{js,js.map,d.ts} private-ssr-support.{js,js.map,d.ts} directives/ development/",
"dev": "scripts/dev.sh",
"prepublishOnly": "npm run check-version",
"test": "npm run test:dev && npm run test:prod",
"test:dev": "cd ../tests && npx wtr '../lit-html/development/**/*_test.js'",
"test:prod": "TEST_PROD_BUILD=true npm run test:dev",
"test:watch": "npm run test:dev -- --watch",
"format": "prettier src/* --write",
"checksize": "rollup -c --environment=CHECKSIZE",
"check-version": "node scripts/check-version-tracker.js",
"prepublishOnly": "npm run check-version"
"test:dev": "cd ../tests && npx wtr '../lit-html/development/**/*_test.(js|html)'",
"test:prod": "MODE=prod npm run test:dev",
"test:watch": "npm run test:dev -- --watch"
},
"author": "The Polymer Authors",
"files": [
"/development/",
"!/development/test/",
"/directives/",
"/lit-html.d.ts",
"/lit-html.d.ts.map",
"/lit-html.js",
"/lit-html.js.map",
"/directive.d.ts",
"/directive.d.ts.map",
"/directive.js",
"/directive.js.map",
"/directive-helpers.d.ts",
"/directive-helpers.d.ts.map",
"/directive-helpers.js",
"/directive-helpers.js.map",
"/disconnectable-directive.d.ts",
"/disconnectable-directive.d.ts.map",
"/disconnectable-directive.js",
"/disconnectable-directive.js.map",
"/platform-support.d.ts",
"/platform-support.d.ts.map",
"/platform-support.js",
"/platform-support.js.map",
"/hydrate.js",
"/hydrate.js.map",
"/hydrate.d.ts",
"/hydrate.d.ts.map",
"/private-ssr-support.js",
"/private-ssr-support.js.map",
"/private-ssr-support.d.ts",
"/private-ssr-support.d.ts.map",
"/src/",
"!/src/test/"
],
"dependencies": {},
"devDependencies": {
"@esm-bundle/chai": "^4.1.5",
"@rollup/plugin-replace": "^2.3.3",
"@types/mocha": "^8.0.3",
"@types/trusted-types": "^1.0.1",
"@web/test-runner-mocha": "^0.3.5",
"@webcomponents/shadycss": "^1.8.0",
"@webcomponents/webcomponentsjs": "^2.5.0",
"chokidar-cli": "^2.1.0",
"concurrently": "^5.3.0",
"mocha": "^8.1.3",
"prettier": "^2.1.1",
"rollup": "^2.26.9",
"rollup-plugin-copy": "^3.3.0",
"rollup-plugin-filesize": "^9.0.2",
"rollup-plugin-sourcemaps": "^0.6.2",
"rollup-plugin-terser": "^7.0.1",
"terser": "^5.2.1",
"typescript": "^4.0.2"
"rollup": "^2.28.2",
"typescript": "^4.1.3"
},
"typings": "lit-html.d.ts",
"directories": {
"test": "test"
}
}
# lit-html 2.0 Pre-release
Efficient, Expressive, Extensible HTML templates in JavaScript
[![Build Status](https://github.com/polymer/lit-html/workflows/Tests/badge.svg?branch=lit-next
)](https://github.com/Polymer/lit-html/actions?query=workflow%3ATests)
[![Build Status](https://github.com/polymer/lit-html/workflows/Tests/badge.svg?branch=lit-next)](https://github.com/Polymer/lit-html/actions?query=workflow%3ATests)
[![Published on npm](https://img.shields.io/npm/v/lit-html/next-major)](https://www.npmjs.com/package/lit-html)

@@ -26,7 +26,8 @@ [![Join our Slack](https://img.shields.io/badge/slack-join%20chat-4a154b.svg)](https://www.polymer-project.org/slack-invite)

changes:
* New `directive` and `Part` API (see below for migration info)
* `render()` no longer clears its container on first render
* Custom `templateFactory`, `TemplateProcessor`, and custom tag functions are no
longer supported
- New `directive` and `Part` API (see below for migration info)
- `render()` no longer clears its container on first render
- Custom `templateFactory`, `TemplateProcessor`, and custom tag functions are no
longer supported
See the full [changelog](CHANGELOG.md) for more details on

@@ -37,11 +38,10 @@ these and other minor breaking changes.

* **Browser support**: This pre-release should run on modern browsers, however a
- **Browser support**: This pre-release should run on modern browsers, however a
change to factor legacy browser support (IE11, etc.) into an opt-in package is
ongoing. As such, this release will not run on some older browsers. This is a
temporary state.
* **Limited directive implementation**: The following directives are not yet
- **Limited directive implementation**: The following directives are not yet
implemented. This is a temporary state:
* `asyncAppend`
* `asyncReplace`
* `until`
- `asyncAppend`
- `asyncReplace`

@@ -56,5 +56,2 @@ ## 🚨 Migrating directives

**⚠️ WARNING: The directive and part API changes are in progress and subject to
change in future pre-releases.**
<details>

@@ -64,11 +61,13 @@ <summary>Expand here for details on migrating directives.</summary>

### Overview of directive API changes
| | 1.x API | 2.0 API |
|-|-----|-----|
| Code idiom for directive | function that takes directive arguments, and returns function that takes `part` and returns value | class with `update` & `render` methods which accept directive arguments |
| Where to do declarative rendering | pass value to `part.setValue()` | return value from `render()` method |
| Where to do imperative DOM/part manipulation | directive function | `update()` method |
| Where state is stored between renders | `WeakMap` keyed on `part` | class instance fields |
| How part validation is done | `instanceof` check on `part` in every render | `part.type` check in constructor
| | 1.x API | 2.0 API |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| Code idiom for directive | function that takes directive arguments, and returns function that takes `part` and returns value | class with `update` & `render` methods which accept directive arguments |
| Where to do declarative rendering | pass value to `part.setValue()` | return value from `render()` method |
| Where to do imperative DOM/part manipulation | directive function | `update()` method |
| Where state is stored between renders | `WeakMap` keyed on `part` | class instance fields |
| How part validation is done | `instanceof` check on `part` in every render | `part.type` check in constructor |
### Example directive migration
Below is an example of a lit-html 1.x directive, and how to migrate it to the

@@ -78,2 +77,3 @@ new API:

1.x Directive API:
```js

@@ -107,32 +107,37 @@ import {directive, NodePart, html} from 'lit-html';

2.0 Directive API:
```js
import {directive, Directive, NODE_PART, html} from 'lit-html';
import {html} from 'lit-html';
import {directive, Directive, PartType} from 'lit-html/directive.js';
// Class-based directive API
export const renderCounter = directive(class extends Directive {
// State stored in class field
value = undefined;
constructor(part) {
super();
// When necessary, validate part in constructor using `part.type`
if (part.type !== NODE_PART) {
throw new Error('renderCounter only supports NodePart');
export const renderCounter = directive(
class extends Directive {
// State stored in class field
value = undefined;
constructor(partInfo: PartInfo, index?: number) {
super(partInfo, index);
// When necessary, validate part in constructor using `part.type`
if (partInfo.type !== PartType.CHILD) {
throw new Error('renderCounter only supports child expressions');
}
}
}
// Any imperative updates to DOM/parts would go here
update(part, [initialValue]) {
// ...
}
// Do SSR-compatible rendering (arguments are passed from call site)
render(initialValue) {
// Previous state available on class field
if (this.value === undefined) {
this.value = initialValue;
} else {
this.value++;
// Any imperative updates to DOM/parts would go here
update(part, [initialValue]) {
// ...
}
return html`<p>${this.value}</p>`;
// Do SSR-compatible rendering (arguments are passed from call site)
render(initialValue) {
// Previous state available on class field
if (this.value === undefined) {
this.value = initialValue;
} else {
this.value++;
}
return html`<p>${this.value}</p>`;
}
}
});
);
```
</details>

@@ -165,4 +170,4 @@

* `html`: A JavaScript [template tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) used to produce a `TemplateResult`, which is a container for a template, and the values that should populate the template.
* `render()`: A function that renders a `TemplateResult` to a DOM container, such as an element or shadow root.
- `html`: A JavaScript [template tag](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals) used to produce a `TemplateResult`, which is a container for a template, and the values that should populate the template.
- `render()`: A function that renders a `TemplateResult` to a DOM container, such as an element or shadow root.

@@ -175,4 +180,50 @@ ## Installation

## Development mode
lit-html includes a development mode which adds additional checks that are
reported in the console.
To enable development mode, add the `development` exports condition to your node
resolve configuration.
#### @web/dev-server
> NOTE: Requires [rollup#540](https://github.com/rollup/plugins/pull/540)
```js
{
nodeResolve: {
exportConditions: ['development'];
}
}
```
#### Rollup
> NOTE: Requires [rollup#540](https://github.com/rollup/plugins/pull/540)
```js
{
plugins: [
nodeResolve({
exportConditions: ['development'],
}),
];
}
```
#### Webpack
> NOTE: Requires [Webpack v5](https://webpack.js.org/migrate/5/)
```js
{
resolve: {
conditionNames: ['development'];
}
}
```
## Contributing
Please see [CONTRIBUTING.md](../../CONTRIBUTING.md).

@@ -15,4 +15,16 @@ /**

import {directive, TemplateResult, NodePart, Directive} from '../lit-html.js';
import {detachNodePart, restoreNodePart, NodePartState} from '../parts.js';
import {TemplateResult, ChildPart, render, nothing} from '../lit-html.js';
import {
directive,
Directive,
DirectiveParameters,
PartInfo,
} from '../directive.js';
import {
clearPart,
getComittedValue,
insertPart,
isTemplateResult,
setComittedValue,
} from '../directive-helpers.js';

@@ -35,28 +47,56 @@ /**

class extends Directive {
templateCache = new WeakMap<TemplateStringsArray, NodePartState>();
templateCache = new WeakMap<TemplateStringsArray, ChildPart>();
value?: TemplateResult;
constructor(partInfo: PartInfo) {
super(partInfo);
}
render(v: unknown) {
return v;
// Return an array of the value to induce lit-html to create a ChildPart
// for the value that we can move into the cache.
return [v];
}
update(part: NodePart, [v]: Parameters<this['render']>) {
// If the new value is not a TemplateResult from the same Template as the
// previous value, move the nodes from the DOM into the cache.
update(containerPart: ChildPart, [v]: DirectiveParameters<this>) {
// If the previous value is a TemplateResult and the new value is not,
// or is a different Template as the previous value, move the child part
// into the cache.
if (
this.value !== undefined &&
this.value.strings !== (v as TemplateResult).strings
isTemplateResult(this.value) &&
(!isTemplateResult(v) || this.value.strings !== v.strings)
) {
this.templateCache.set(this.value.strings, detachNodePart(part));
// This is always an array because we return [v] in render()
const partValue = getComittedValue(containerPart) as Array<ChildPart>;
const childPart = partValue.pop()!;
let cachedContainerPart = this.templateCache.get(this.value.strings);
if (cachedContainerPart === undefined) {
const fragment = document.createDocumentFragment();
cachedContainerPart = render(nothing, fragment);
this.templateCache.set(this.value.strings, cachedContainerPart);
}
// Move into cache
setComittedValue(cachedContainerPart, [childPart]);
insertPart(cachedContainerPart, undefined, childPart);
}
// If the new value is a TemplateResult, try to restore it from cache
if ((v as TemplateResult)._$litType$ !== undefined) {
let cachedTemplate = this.templateCache.get(
(v as TemplateResult).strings
);
if (cachedTemplate !== undefined) {
restoreNodePart(part, cachedTemplate);
// If the new value is a TemplateResult and the previous value is not,
// or is a different Template as the previous value, restore the child
// part from the cache.
if (
isTemplateResult(v) &&
(!isTemplateResult(this.value) || this.value.strings !== v.strings)
) {
const cachedContainerPart = this.templateCache.get(v.strings);
if (cachedContainerPart !== undefined) {
// Move the cached part back into the container part value
const partValue = getComittedValue(
cachedContainerPart
) as Array<ChildPart>;
const cachedPart = partValue.pop()!;
// Move cached part back into DOM
clearPart(containerPart);
insertPart(containerPart, undefined, cachedPart);
setComittedValue(containerPart, [cachedPart]);
}
this.value = v as TemplateResult;
this.value = v;
} else {

@@ -63,0 +103,0 @@ this.value = undefined;

@@ -15,10 +15,10 @@ /**

import {AttributePart, noChange} from '../lit-html.js';
import {
AttributePart,
directive,
Directive,
noChange,
DirectiveParameters,
PartInfo,
ATTRIBUTE_PART,
} from '../lit-html.js';
PartType,
} from '../directive.js';

@@ -39,8 +39,8 @@ /**

constructor(part: PartInfo) {
super();
constructor(partInfo: PartInfo) {
super(partInfo);
if (
part.type !== ATTRIBUTE_PART ||
part.name !== 'class' ||
(part.strings !== undefined && part.strings.length > 2)
partInfo.type !== PartType.ATTRIBUTE ||
partInfo.name !== 'class' ||
(partInfo.strings?.length as number) > 2
) {

@@ -60,3 +60,3 @@ throw new Error(

update(part: AttributePart, [classInfo]: [ClassInfo]) {
update(part: AttributePart, [classInfo]: DirectiveParameters<this>) {
// Remember dynamic classes on the first render

@@ -116,4 +116,4 @@ if (this.previousClasses === undefined) {

*
* @param classInfo {ClassInfo}
* @param classInfo
*/
export const classMap = directive(ClassMap);

@@ -15,3 +15,4 @@ /**

import {directive, Directive, noChange, Part} from '../lit-html.js';
import {noChange, Part} from '../lit-html.js';
import {directive, Directive, DirectiveParameters} from '../directive.js';

@@ -28,3 +29,3 @@ // A sentinal that indicates guard() hasn't rendered anything yet

update(_part: Part, [value, f]: Parameters<this['render']>) {
update(_part: Part, [value, f]: DirectiveParameters<this>) {
if (Array.isArray(value)) {

@@ -31,0 +32,0 @@ // Dirty-check arrays by item

@@ -15,29 +15,27 @@ /**

import {AttributePart, noChange, nothing} from '../lit-html.js';
import {
directive,
Directive,
AttributePart,
noChange,
nothing,
DirectiveParameters,
PartInfo,
NODE_PART,
EVENT_PART,
PROPERTY_PART,
BOOLEAN_ATTRIBUTE_PART,
ATTRIBUTE_PART,
} from '../lit-html.js';
PartType,
} from '../directive.js';
import {isSingleExpression, setComittedValue} from '../directive-helpers.js';
// A sentinal value that can never appear as a part value except when set by
// live(). Used to force a dirty-check to fail and cause a re-render.
const RESET_VALUE = {};
class LiveDirective extends Directive {
constructor(part: PartInfo) {
super();
if (part.type === EVENT_PART || part.type === NODE_PART) {
constructor(partInfo: PartInfo) {
super(partInfo);
if (
!(
partInfo.type === PartType.PROPERTY ||
partInfo.type === PartType.ATTRIBUTE ||
partInfo.type === PartType.BOOLEAN_ATTRIBUTE
)
) {
throw new Error(
'The `live` directive is not allowed on text or event bindings'
'The `live` directive is not allowed on child or event bindings'
);
}
if (part.strings !== undefined) {
if (!isSingleExpression(partInfo)) {
throw new Error('`live` bindings can only contain a single expression');

@@ -51,4 +49,4 @@ }

update(part: AttributePart, [value]: Parameters<this['render']>) {
if (value === noChange) {
update(part: AttributePart, [value]: DirectiveParameters<this>) {
if (value === noChange || value === nothing) {
return value;

@@ -62,3 +60,3 @@ }

// interface?
if (part.type === PROPERTY_PART) {
if (part.type === PartType.PROPERTY) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any

@@ -68,9 +66,7 @@ if (value === (element as any)[name]) {

}
} else if (part.type === BOOLEAN_ATTRIBUTE_PART) {
if (
(value === nothing ? false : !!value) === element.hasAttribute(name)
) {
} else if (part.type === PartType.BOOLEAN_ATTRIBUTE) {
if (!!value === element.hasAttribute(name)) {
return noChange;
}
} else if (part.type === ATTRIBUTE_PART) {
} else if (part.type === PartType.ATTRIBUTE) {
if (element.getAttribute(name) === String(value)) {

@@ -80,5 +76,5 @@ return noChange;

}
// Setting the part's value to RESET_VALUE causes its dirty-check to fail
// so that it always sets the value.
part._value = RESET_VALUE;
// Resets the part's value, causing its dirty-check to fail so that it
// always sets the value.
setComittedValue(part);
return value;

@@ -99,4 +95,4 @@ }

* bindings hasn't, lit-html won't know to update the DOM value and will leave
* it alone. If this is not what you want—if you want to overwrite the DOM
* value with the bound value no matter what—use the `live()` directive:
* it alone. If this is not what you want--if you want to overwrite the DOM
* value with the bound value no matter what--use the `live()` directive:
*

@@ -103,0 +99,0 @@ * html`<input .value=${live(x)}>`

@@ -15,16 +15,11 @@ /**

import {ChildPart, noChange} from '../lit-html.js';
import {directive, Directive, PartInfo, PartType} from '../directive.js';
import {
directive,
NodePart,
Directive,
noChange,
PartInfo,
} from '../lit-html.js';
import {
createAndInsertPart,
getPartValue,
insertPartBefore,
insertPart,
getComittedValue,
removePart,
setPartValue,
} from '../parts.js';
setComittedValue,
setChildPartValue,
} from '../directive-helpers.js';

@@ -68,5 +63,5 @@ export type KeyFn<T> = (item: T, index: number) => unknown;

constructor(part: PartInfo) {
super();
if (!(part instanceof NodePart)) {
constructor(partInfo: PartInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error('repeat can only be used in text bindings');

@@ -76,3 +71,3 @@ }

_getValuesAndKeys<T>(
private _getValuesAndKeys<T>(
items: Iterable<T>,

@@ -111,3 +106,3 @@ keyFnOrTemplate: KeyFn<T> | ItemTemplate<T>,

update<T>(
containerPart: NodePart,
containerPart: ChildPart,
[items, keyFnOrTemplate, template]: [

@@ -121,3 +116,3 @@ Iterable<T>,

// TODO: deal with directive being swapped out?
let oldParts = getPartValue(containerPart) as Array<NodePart | null>;
const oldParts = getComittedValue(containerPart) as Array<ChildPart | null>;
const {values: newValues, keys: newKeys} = this._getValuesAndKeys(

@@ -139,3 +134,3 @@ items,

// saved in the above cache at the end of the update.
const newParts: NodePart[] = [];
const newParts: ChildPart[] = [];

@@ -362,3 +357,3 @@ // Maps from key to index for current and previous update; these

// Old head matches new head; update in place
newParts[newHead] = setPartValue(
newParts[newHead] = setChildPartValue(
oldParts[oldHead]!,

@@ -371,3 +366,3 @@ newValues[newHead]

// Old tail matches new tail; update in place
newParts[newTail] = setPartValue(
newParts[newTail] = setChildPartValue(
oldParts[oldTail]!,

@@ -380,11 +375,7 @@ newValues[newTail]

// Old head matches new tail; update and move to new tail
newParts[newTail] = setPartValue(
newParts[newTail] = setChildPartValue(
oldParts[oldHead]!,
newValues[newTail]
);
insertPartBefore(
containerPart,
oldParts[oldHead]!,
newParts[newTail + 1]
);
insertPart(containerPart, newParts[newTail + 1], oldParts[oldHead]!);
oldHead++;

@@ -394,7 +385,7 @@ newTail--;

// Old tail matches new head; update and move to new head
newParts[newHead] = setPartValue(
newParts[newHead] = setChildPartValue(
oldParts[oldTail]!,
newValues[newHead]
);
insertPartBefore(containerPart, oldParts[oldTail]!, oldParts[oldHead]!);
insertPart(containerPart, oldParts[oldHead]!, oldParts[oldTail]!);
oldTail--;

@@ -426,12 +417,9 @@ newHead++;

// insert it
const newPart = createAndInsertPart(
containerPart,
oldParts[oldHead]!
);
setPartValue(newPart, newValues[newHead]);
const newPart = insertPart(containerPart, oldParts[oldHead]!);
setChildPartValue(newPart, newValues[newHead]);
newParts[newHead] = newPart;
} else {
// Reuse old part
newParts[newHead] = setPartValue(oldPart, newValues[newHead]);
insertPartBefore(containerPart, oldPart, oldParts[oldHead]!);
newParts[newHead] = setChildPartValue(oldPart, newValues[newHead]);
insertPart(containerPart, oldParts[oldHead]!, oldPart);
// This marks the old part as having been used, so that

@@ -449,4 +437,4 @@ // it will be skipped in the first two checks above

// tail, since old pointers are no longer valid
const newPart = createAndInsertPart(containerPart, newParts[newTail + 1]);
setPartValue(newPart, newValues[newHead]);
const newPart = insertPart(containerPart, newParts[newTail + 1]);
setChildPartValue(newPart, newValues[newHead]);
newParts[newHead++] = newPart;

@@ -465,4 +453,3 @@ }

// Directly set part value, bypassing it's dirty-checking
// TODO (justinfagnani): resolve with https://github.com/Polymer/lit-html/issues/1261
containerPart._value = newParts;
setComittedValue(containerPart, newParts);
return noChange;

@@ -469,0 +456,0 @@ }

@@ -15,10 +15,10 @@ /**

import {AttributePart, noChange} from '../lit-html.js';
import {
AttributePart,
directive,
Directive,
noChange,
DirectiveParameters,
PartInfo,
ATTRIBUTE_PART,
} from '../lit-html.js';
PartType,
} from '../directive.js';

@@ -39,8 +39,8 @@ /**

constructor(part: PartInfo) {
super();
constructor(partInfo: PartInfo) {
super(partInfo);
if (
part.type !== ATTRIBUTE_PART ||
part.name !== 'style' ||
(part.strings !== undefined && part.strings.length > 2)
partInfo.type !== PartType.ATTRIBUTE ||
partInfo.name !== 'style' ||
(partInfo.strings?.length as number) > 2
) {

@@ -74,3 +74,3 @@ throw new Error(

update(part: AttributePart, [styleInfo]: Parameters<this['render']>) {
update(part: AttributePart, [styleInfo]: DirectiveParameters<this>) {
const {style} = part.element as HTMLElement;

@@ -130,4 +130,4 @@

*
* @param styleInfo {StyleInfo}
* @param styleInfo
*/
export const styleMap = directive(StyleMap);

@@ -15,17 +15,12 @@ /**

import {
Directive,
directive,
noChange,
NODE_PART,
PartInfo,
} from '../lit-html.js';
import {noChange} from '../lit-html.js';
import {directive, Directive, PartInfo, PartType} from '../directive.js';
class TemplateContent extends Directive {
private __previousTemplate?: HTMLTemplateElement;
private _previousTemplate?: HTMLTemplateElement;
constructor(part: PartInfo) {
super();
if (part.type !== NODE_PART) {
throw new Error('templateContent can only be used in text bindings');
constructor(partInfo: PartInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error('templateContent can only be used in child bindings');
}

@@ -35,6 +30,6 @@ }

render(template: HTMLTemplateElement) {
if (this.__previousTemplate === template) {
if (this._previousTemplate === template) {
return noChange;
}
this.__previousTemplate = template;
this._previousTemplate = template;
return document.importNode(template.content, true);

@@ -41,0 +36,0 @@ }

@@ -15,11 +15,4 @@ /**

import {
directive,
Directive,
nothing,
TemplateResult,
noChange,
PartInfo,
NODE_PART,
} from '../lit-html.js';
import {nothing, TemplateResult, noChange} from '../lit-html.js';
import {directive, Directive, PartInfo, PartType} from '../directive.js';

@@ -35,9 +28,9 @@ const HTML_RESULT = 1;

constructor(part: PartInfo) {
super();
if (part.type !== NODE_PART) {
constructor(partInfo: PartInfo) {
super(partInfo);
if (partInfo.type !== PartType.CHILD) {
throw new Error(
`${
(this.constructor as typeof UnsafeHTML).directiveName
}() can only be used in text bindings`
}() can only be used in child bindings`
);

@@ -47,3 +40,3 @@ }

render(value: string) {
render(value: string | typeof nothing | typeof noChange) {
// TODO: add tests for nothing and noChange

@@ -69,2 +62,3 @@ if (value === nothing) {

const strings = ([value] as unknown) as TemplateStringsArray;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(strings as any).raw = strings;

@@ -71,0 +65,0 @@ // WARNING: impersonating a TemplateResult like this is extremely

@@ -15,3 +15,3 @@ /**

import {directive} from '../lit-html.js';
import {directive} from '../directive.js';
import {UnsafeHTML} from './unsafe-html.js';

@@ -18,0 +18,0 @@

@@ -15,3 +15,7 @@ /**

// IMPORTANT: these imports must be type-only
import {Directive, DirectiveResult, PartInfo} from './directive.js';
const DEV_MODE = true;
const ENABLE_EXTRA_SECURITY_HOOKS = true;

@@ -22,2 +26,74 @@ if (DEV_MODE) {

/**
* Used to sanitize any value before it is written into the DOM. This can be
* used to implement a security policy of allowed and disallowed values in
* order to prevent XSS attacks.
*
* One way of using this callback would be to check attributes and properties
* against a list of high risk fields, and require that values written to such
* fields be instances of a class which is safe by construction. Closure's Safe
* HTML Types is one implementation of this technique (
* https://github.com/google/safe-html-types/blob/master/doc/safehtml-types.md).
* The TrustedTypes polyfill in API-only mode could also be used as a basis
* for this technique (https://github.com/WICG/trusted-types).
*
* @param node The HTML node (usually either a #text node or an Element) that
* is being written to. Note that this is just an exemplar node, the write
* may take place against another instance of the same class of node.
* @param name The name of an attribute or property (for example, 'href').
* @param type Indicates whether the write that's about to be performed will
* be to a property or a node.
* @return A function that will sanitize this class of writes.
*/
export type SanitizerFactory = (
node: Node,
name: string,
type: 'property' | 'attribute'
) => ValueSanitizer;
/**
* A function which can sanitize values that will be written to a specific kind
* of DOM sink.
*
* See SanitizerFactory.
*
* @param value The value to sanitize. Will be the actual value passed into
* the lit-html template literal, so this could be of any type.
* @return The value to write to the DOM. Usually the same as the input value,
* unless sanitization is needed.
*/
export type ValueSanitizer = (value: unknown) => unknown;
const identityFunction: ValueSanitizer = (value: unknown) => value;
const noopSanitizer: SanitizerFactory = (
_node: Node,
_name: string,
_type: 'property' | 'attribute'
) => identityFunction;
/** Sets the global sanitizer factory. */
const setSanitizer = (newSanitizer: SanitizerFactory) => {
if (!ENABLE_EXTRA_SECURITY_HOOKS) {
return;
}
if (sanitizerFactoryInternal !== noopSanitizer) {
throw new Error(
`Attempted to overwrite existing lit-html security policy.` +
` setSanitizeDOMValueFactory should be called at most once.`
);
}
sanitizerFactoryInternal = newSanitizer;
};
/**
* Only used in internal tests, not a part of the public API.
*/
const _testOnlyClearSanitizerFactoryDoNotCallOrElse = () => {
sanitizerFactoryInternal = noopSanitizer;
};
const createSanitizer: SanitizerFactory = (node, name, type) => {
return sanitizerFactoryInternal(node, name, type);
};
// Added to an attribute name to mark the attribute as bound so we can find

@@ -52,3 +128,4 @@ // it easily.

isArray(value) ||
(value && typeof (value as any)[Symbol.iterator] === 'function');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typeof (value as any)?.[Symbol.iterator] === 'function';

@@ -91,3 +168,4 @@ // TODO (justinfagnani): can we get away with `\s`?

* The tagEnd regex matches the end of the "inside an opening" tag syntax
* position. It either matches a `>` or an attribute.
* position. It either matches a `>`, an attribute-like sequence, or the end
* of the string after a space (attribute-name position ending).
*

@@ -115,3 +193,3 @@ * See attributes in the HTML spec:

const tagEndRegex = new RegExp(
`>|${SPACE_CHAR}(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))`,
`>|${SPACE_CHAR}(?:(${NAME_CHAR}+)(${SPACE_CHAR}*=${SPACE_CHAR}*(?:${ATTR_VALUE_CHAR}|("|')|))|$)`,
'g'

@@ -138,15 +216,14 @@ );

/** TemplatePart types */
// TODO (justinfagnani): since these are exported, consider shorter names,
// like just `ATTRIBUTE`.
export const ATTRIBUTE_PART = 1;
export const NODE_PART = 2;
export const PROPERTY_PART = 3;
export const BOOLEAN_ATTRIBUTE_PART = 4;
export const EVENT_PART = 5;
type ResultType = typeof HTML_RESULT | typeof SVG_RESULT;
// TemplatePart types
// IMPORTANT: these must match the values in PartType
const ATTRIBUTE_PART = 1;
const CHILD_PART = 2;
const PROPERTY_PART = 3;
const BOOLEAN_ATTRIBUTE_PART = 4;
const EVENT_PART = 5;
const ELEMENT_PART = 6;
const COMMENT_PART = 7;
type ResultType = typeof HTML_RESULT | typeof SVG_RESULT;
/**

@@ -194,8 +271,8 @@ * The return type of the template tag functions.

*/
export const noChange = {};
export const noChange = Symbol.for('lit-noChange');
/**
* A sentinel value that signals a NodePart to fully clear its content.
* A sentinel value that signals a ChildPart to fully clear its content.
*/
export const nothing = {};
export const nothing = Symbol.for('lit-nothing');

@@ -211,58 +288,2 @@ /**

export type NodePartInfo = {
readonly type: typeof NODE_PART;
};
export type AttributePartInfo = {
readonly type:
| typeof ATTRIBUTE_PART
| typeof PROPERTY_PART
| typeof BOOLEAN_ATTRIBUTE_PART
| typeof EVENT_PART;
strings?: ReadonlyArray<string>;
name: string;
tagName: string;
};
/**
* Information about the part a directive is bound to.
*
* This is useful for checking that a directive is attached to a valid part,
* such as with directive that can only be used on attribute bindings.
*/
export type PartInfo = NodePartInfo | AttributePartInfo;
export type DirectiveClass = {new (part: PartInfo): Directive};
/**
* This utility type extracts the signature of a directive class's render()
* method so we can use it for the type of the generated directive function.
*/
export type DirectiveParameters<C extends DirectiveClass> = Parameters<
InstanceType<C>['render']
>;
/**
* A generated directive function doesn't evaluate the directive, but just
* returns a DirectiveResult object that captures the arguments.
*/
type DirectiveResult<C extends DirectiveClass = DirectiveClass> = {
_$litDirective$: C;
values: DirectiveParameters<C>;
};
/**
* Creates a user-facing directive function from a Directive class. This
* function has the same parameters as the directive's render() method.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
*/
export const directive = <C extends DirectiveClass>(c: C) => (
...values: DirectiveParameters<C>
): DirectiveResult<C> => ({
_$litDirective$: c,
values,
});
export interface RenderOptions {

@@ -273,7 +294,7 @@ /**

*/
readonly eventContext?: EventTarget;
host?: EventTarget;
/**
* A DOM node before which to render content in the container.
*/
readonly renderBefore?: ChildNode | null;
renderBefore?: ChildNode | null;
}

@@ -291,18 +312,37 @@

options?: RenderOptions
) => {
): ChildPart => {
const partOwnerNode = options?.renderBefore ?? container;
let part: NodePart = (partOwnerNode as any).$lit$;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let part: ChildPart = (partOwnerNode as any)._$litPart;
if (part === undefined) {
const endNode = options?.renderBefore ?? null;
(partOwnerNode as any).$lit$ = part = new NodePart(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(partOwnerNode as any)._$litPart = part = new ChildPartImpl(
container.insertBefore(createMarker(), endNode),
endNode,
undefined,
options
);
}
part._setValue(value);
part._$setValue(value);
return part;
};
const walker = d.createTreeWalker(d);
if (ENABLE_EXTRA_SECURITY_HOOKS) {
render.setSanitizer = setSanitizer;
render.createSanitizer = createSanitizer;
if (DEV_MODE) {
render._testOnlyClearSanitizerFactoryDoNotCallOrElse = _testOnlyClearSanitizerFactoryDoNotCallOrElse;
}
}
const walker = d.createTreeWalker(
d,
133 /* NodeFilter.SHOW_{ELEMENT|COMMENT|TEXT} */,
null,
false
);
let sanitizerFactoryInternal: SanitizerFactory = noopSanitizer;
//

@@ -315,166 +355,194 @@ // Classes only below here, const variable declarations only above here...

// Type for classes that have a `_directive` or `_directives[]` field, used by
// `resolveDirective`
export interface DirectiveParent {
_$parent?: DirectiveParent;
__directive?: Directive;
__directives?: Array<Directive | undefined>;
}
/**
* Base class for creating custom directives. Users should extend this class,
* implement `render` and/or `update`, and then pass their subclass to
* `directive`.
* Returns an HTML string for the given TemplateStringsArray and result type
* (HTML or SVG), along with the case-sensitive bound attribute names in
* template order. The HTML contains comment comment markers denoting the
* `ChildPart`s and suffixes on bound attributes denoting the `AttributeParts`.
*
* WARNING: The directive and part API changes are in progress and subject to
* change in future pre-releases.
* @param strings template strings array
* @param type HTML or SVG
* @return Array containing `[html, attrNames]` (array returned for terseness,
* to avoid object fields since this code is shared with non-minified SSR
* code)
*/
export abstract class Directive {
abstract render(...props: Array<unknown>): unknown;
update(_part: Part, props: Array<unknown>): unknown {
return this.render(...props);
}
}
const getTemplateHtml = (
strings: TemplateStringsArray,
type: ResultType
): [string, string[]] => {
// Insert makers into the template HTML to represent the position of
// bindings. The following code scans the template strings to determine the
// syntactic position of the bindings. They can be in text position, where
// we insert an HTML comment, attribute value position, where we insert a
// sentinel string and re-write the attribute name, or inside a tag where
// we insert the sentinel string.
const l = strings.length - 1;
const attrNames: Array<string> = [];
let html = type === SVG_RESULT ? '<svg>' : '';
class Template {
private __strings: TemplateStringsArray;
__element: HTMLTemplateElement;
__parts: Array<TemplatePart> = [];
// When we're inside a raw text tag (not it's text content), the regex
// will still be tagRegex so we can find attributes, but will switch to
// this regex when the tag ends.
let rawTextEndRegex: RegExp | undefined;
constructor({strings, _$litType$: type}: TemplateResult) {
walker.currentNode = (this.__element = d.createElement('template')).content;
// The current parsing state, represented as a reference to one of the
// regexes
let regex = textEndRegex;
// Insert makers into the template HTML to represent the position of
// bindings. The following code scans the template strings to determine the
// syntactic position of the bindings. They can be in text position, where
// we insert an HTML comment, attribute value position, where we insert a
// sentinel string and re-write the attribute name, or inside a tag where
// we insert the sentinel string.
const l = (this.__strings = strings).length - 1;
const attrNames: Array<string> = [];
let html = type === SVG_RESULT ? '<svg>' : '';
let node: Node | null;
let nodeIndex = 0;
let bindingIndex = 0;
let attrNameIndex = 0;
for (let i = 0; i < l; i++) {
const s = strings[i];
// The index of the end of the last attribute name. When this is
// positive at end of a string, it means we're in an attribute value
// position and need to rewrite the attribute name.
// We also use a special value of -2 to indicate that we encountered
// the end of a string in attribute name position.
let attrNameEndIndex = -1;
let attrName: string | undefined;
let lastIndex = 0;
let match!: RegExpExecArray | null;
// When we're inside a raw text tag (not it's text content), the regex
// will still be tagRegex so we can find attributes, but will switch to
// this regex when the tag ends.
let rawTextEndRegex: RegExp | undefined;
// The current parsing state, represented as a reference to one of the
// regexes
let regex = textEndRegex;
for (let i = 0; i < l; i++) {
const s = strings[i];
// The index of the end of the last attribute name. When this is
// positive at end of a string, it means we're in an attribute value
// position and need to rewrite the attribute name.
let attrNameEndIndex = -1;
let attrName: string | undefined;
let lastIndex = 0;
let match: RegExpExecArray | null;
// The conditions in this loop handle the current parse state, and the
// assignments to the `regex` variable are the state transitions.
while (lastIndex < s.length) {
// Make sure we start searching from where we previously left off
regex.lastIndex = lastIndex;
match = regex.exec(s);
if (match === null) {
// If the current regex doesn't match we've come to a binding inside
// that state and must break and insert a marker
if (regex === tagEndRegex) {
// When tagEndRegex doesn't match we must have a binding in
// attribute-name position, since tagEndRegex does match static
// attribute names and end-of-tag. We need to clear
// attrNameEndIndex which may have been set by a previous
// tagEndRegex match.
attrNameEndIndex = -1;
// The conditions in this loop handle the current parse state, and the
// assignments to the `regex` variable are the state transitions.
while (lastIndex < s.length) {
// Make sure we start searching from where we previously left off
regex.lastIndex = lastIndex;
match = regex.exec(s);
if (match === null) {
break;
}
lastIndex = regex.lastIndex;
if (regex === textEndRegex) {
if (match[COMMENT_START] === '!--') {
regex = commentEndRegex;
} else if (match[COMMENT_START] !== undefined) {
// We started a weird comment, like </{
regex = comment2EndRegex;
} else if (match[TAG_NAME] !== undefined) {
if (rawTextElement.test(match[TAG_NAME])) {
// Record if we encounter a raw-text element. We'll switch to
// this regex at the end of the tag.
rawTextEndRegex = new RegExp(`</${match[TAG_NAME]}`, 'g');
}
break;
}
lastIndex = regex.lastIndex;
if (regex === textEndRegex) {
if (match[COMMENT_START] === '!--') {
regex = commentEndRegex;
} else if (match[COMMENT_START] !== undefined) {
// We started a weird comment, like </{
regex = comment2EndRegex;
} else if (match[TAG_NAME] !== undefined) {
if (rawTextElement.test(match[TAG_NAME])) {
// Record if we encounter a raw-text element. We'll switch to
// this regex at the end of the tag
rawTextEndRegex = new RegExp(`</${match[TAG_NAME]}`, 'g');
}
regex = tagEndRegex;
} else if (match[DYNAMIC_TAG_NAME] !== undefined) {
// dynamic tag name
regex = tagEndRegex;
}
} else if (regex === tagEndRegex) {
if (match[ENTIRE_MATCH] === '>') {
// End of a tag. If we had started a raw-text element, use that
// regex
regex = rawTextEndRegex ?? textEndRegex;
// We may be ending an unquoted attribute value, so make sure we
// clear any pending attrNameEndIndex
attrNameEndIndex = -1;
} else {
attrNameEndIndex =
regex.lastIndex - match[SPACES_AND_EQUALS].length;
attrName = match[ATTRIBUTE_NAME];
regex =
match[QUOTE_CHAR] === undefined
? tagEndRegex
: match[QUOTE_CHAR] === '"'
? doubleQuoteAttrEndRegex
: singleQuoteAttrEndRegex;
}
} else if (
regex === doubleQuoteAttrEndRegex ||
regex === singleQuoteAttrEndRegex
) {
regex = tagEndRegex;
} else if (regex === commentEndRegex || regex === comment2EndRegex) {
regex = textEndRegex;
} else {
// Not one of the five state regexes, so it must be the dynamically
// created raw text regex and we're at the close of that element.
} else if (match[DYNAMIC_TAG_NAME] !== undefined) {
// dynamic tag name
regex = tagEndRegex;
rawTextEndRegex = undefined;
}
} else if (regex === tagEndRegex) {
if (match[ENTIRE_MATCH] === '>') {
// End of a tag. If we had started a raw-text element, use that
// regex
regex = rawTextEndRegex ?? textEndRegex;
// We may be ending an unquoted attribute value, so make sure we
// clear any pending attrNameEndIndex
attrNameEndIndex = -1;
} else if (match[ATTRIBUTE_NAME] === undefined) {
// Attribute name position
attrNameEndIndex = -2;
} else {
attrNameEndIndex = regex.lastIndex - match[SPACES_AND_EQUALS].length;
attrName = match[ATTRIBUTE_NAME];
regex =
match[QUOTE_CHAR] === undefined
? tagEndRegex
: match[QUOTE_CHAR] === '"'
? doubleQuoteAttrEndRegex
: singleQuoteAttrEndRegex;
}
} else if (
regex === doubleQuoteAttrEndRegex ||
regex === singleQuoteAttrEndRegex
) {
regex = tagEndRegex;
} else if (regex === commentEndRegex || regex === comment2EndRegex) {
regex = textEndRegex;
} else {
// Not one of the five state regexes, so it must be the dynamically
// created raw text regex and we're at the close of that element.
regex = tagEndRegex;
rawTextEndRegex = undefined;
}
}
if (DEV_MODE) {
// If we have a attrNameEndIndex, which indicates that we should
// rewrite the attribute name, assert that we're in a valid attribute
// position - either in a tag, or a quoted attribute value.
console.assert(
attrNameEndIndex === -1 ||
regex === tagEndRegex ||
regex === singleQuoteAttrEndRegex ||
regex === doubleQuoteAttrEndRegex,
'unexpected parse state B'
);
}
// If we're in text position, and not in a raw text element
// (regex === textEndRegex), we insert a comment marker. Otherwise, we
// insert a plain maker. If we have a attrNameEndIndex, it means we need
// to rewrite the attribute name to add a bound attribute suffix.
html +=
regex === textEndRegex
? s + nodeMarker
: (attrNameEndIndex !== -1
? (attrNames.push(attrName!),
s.slice(0, attrNameEndIndex) +
boundAttributeSuffix +
s.slice(attrNameEndIndex))
: s) + marker;
if (DEV_MODE) {
// If we have a attrNameEndIndex, which indicates that we should
// rewrite the attribute name, assert that we're in a valid attribute
// position - either in a tag, or a quoted attribute value.
console.assert(
attrNameEndIndex === -1 ||
regex === tagEndRegex ||
regex === singleQuoteAttrEndRegex ||
regex === doubleQuoteAttrEndRegex,
'unexpected parse state B'
);
}
// TODO (justinfagnani): if regex is not textRegex log a warning for a
// malformed template in dev mode.
// We have four cases:
// 1. We're in text position, and not in a raw text element
// (regex === textEndRegex): insert a comment marker.
// 2. We have a non-negative attrNameEndIndex which means we need to
// rewrite the attribute name to add a bound attribute suffix.
// 3. We're at the non-first binding in a multi-binding attribute, use a
// plain marker.
// 4. We're somewhere else inside the tag. If we're in attribute name
// position (attrNameEndIndex === -2), add a sequential suffix to
// generate a unique attribute name.
html +=
regex === textEndRegex
? s + nodeMarker
: attrNameEndIndex >= 0
? (attrNames.push(attrName!),
s.slice(0, attrNameEndIndex) +
boundAttributeSuffix +
s.slice(attrNameEndIndex)) + marker
: s + marker + (attrNameEndIndex === -2 ? `:${i}` : '');
}
// Note, we don't add '</svg>' for SVG result types because the parser
// will close the <svg> tag for us.
this.__element.innerHTML = html + this.__strings[l];
// Returned as an array for terseness
return [
// We don't technically need to close the SVG tag since the parser will
// handle it for us, but the SSR parser doesn't like that.
// Note that the html must end with a node after the final expression to
// ensure the last ChildPart has an end node, hence adding a comment if the
// last string was empty.
html + (strings[l] || '<?>') + (type === SVG_RESULT ? '</svg>' : ''),
attrNames,
];
};
export type Template = Interface<TemplateImpl>;
class TemplateImpl {
/** @internal */
_$element!: HTMLTemplateElement;
/** @internal */
_parts: Array<TemplatePart> = [];
// Note, this is used by the `platform-support` module.
_$options?: RenderOptions;
constructor(
{strings, _$litType$: type}: TemplateResult,
options?: RenderOptions
) {
this._$options = options;
let node: Node | null;
let nodeIndex = 0;
let bindingIndex = 0;
let attrNameIndex = 0;
const l = strings.length - 1;
// Create template element
const [html, attrNames] = getTemplateHtml(strings, type);
this._$element = this._$createElement(html);
walker.currentNode = this._$element.content;
// Reparent SVG nodes into template root
if (type === SVG_RESULT) {
const content = this.__element.content;
const content = this._$element.content;
const svgElement = content.firstChild!;

@@ -492,34 +560,49 @@ svgElement.remove();

if ((node as Element).hasAttributes()) {
const {attributes} = node as Element;
for (let i = 0; i < attributes.length; i++) {
const {name, value} = attributes[i];
// We defer removing bound attributes because on IE we might not be
// iterating attributes in their template order, and would sometimes
// remove an attribute that we still need to create a part for.
const attrsToRemove = [];
for (const name of (node as Element).getAttributeNames()) {
// `name` is the name of the attribute we're iterating over, but not
// _neccessarily_ the name of the attribute we will create a part
// for. They can be different in browsers that don't iterate on
// attributes in source order. In that case the attrNames array
// contains the attribute name we'll process next. We only need the
// attribute name here to know if we should process a bound attribute
// on this element.
if (name.endsWith(boundAttributeSuffix)) {
i--;
(node as Element).removeAttribute(name);
const realName = attrNames[attrNameIndex++];
// Lowercase for case-sensitive SVG attributes like viewBox
const value = (node as Element).getAttribute(
realName.toLowerCase() + boundAttributeSuffix
)!;
attrsToRemove.push(name);
const statics = value.split(marker);
const m = /([.?@])?(.*)/.exec(attrNames[attrNameIndex++])!;
this.__parts.push({
__type: ATTRIBUTE_PART,
__index: nodeIndex,
__name: m[2],
__strings: statics,
__constructor:
const m = /([.?@])?(.*)/.exec(realName)!;
this._parts.push({
_type: ATTRIBUTE_PART,
_index: nodeIndex,
_name: m[2],
_strings: statics,
_constructor:
m[1] === '.'
? PropertyPart
? PropertyPartImpl
: m[1] === '?'
? BooleanAttributePart
? BooleanAttributePartImpl
: m[1] === '@'
? EventPart
: AttributePart,
? EventPartImpl
: AttributePartImpl,
});
bindingIndex += statics.length - 1;
} else if (name === marker) {
(node as Element).removeAttribute(name);
i--;
this.__parts.push({
__type: ELEMENT_PART,
__index: nodeIndex,
} else if (name.startsWith(marker)) {
attrsToRemove.push(name);
this._parts.push({
_type: ELEMENT_PART,
_index: nodeIndex,
});
}
}
for (const name of attrsToRemove) {
(node as Element).removeAttribute(name);
}
}

@@ -542,3 +625,3 @@ // TODO (justinfagnani): benchmark the regex against testing for each

(node as Element).append(strings[i] || createMarker());
this.__parts.push({__type: NODE_PART, __index: ++nodeIndex});
this._parts.push({_type: CHILD_PART, _index: ++nodeIndex});
bindingIndex++;

@@ -553,3 +636,3 @@ }

bindingIndex++;
this.__parts.push({__type: NODE_PART, __index: nodeIndex});
this._parts.push({_type: CHILD_PART, _index: nodeIndex});
} else {

@@ -562,3 +645,3 @@ let i = -1;

// make bindings in comments work
this.__parts.push({__type: COMMENT_PART, __index: nodeIndex});
this._parts.push({_type: COMMENT_PART, _index: nodeIndex});
bindingIndex++;

@@ -573,4 +656,54 @@ // Move to the end of the match

}
// Overridden via `litHtmlPlatformSupport` to provide platform support.
_$createElement(html: string) {
const template = d.createElement('template');
template.innerHTML = html;
return template;
}
}
export interface Disconnectable {
_$parent?: Disconnectable;
_$disconnetableChildren?: Set<Disconnectable>;
}
function resolveDirective(
part: ChildPart | AttributePart | ElementPart,
value: unknown,
_$parent: DirectiveParent = part,
_$attributeIndex?: number
): unknown {
let currentDirective =
_$attributeIndex !== undefined
? (_$parent as AttributePart).__directives?.[_$attributeIndex]
: (_$parent as ChildPart | ElementPart | Directive).__directive;
const nextDirectiveConstructor = isPrimitive(value)
? undefined
: (value as DirectiveResult)._$litDirective$;
if (currentDirective?.constructor !== nextDirectiveConstructor) {
currentDirective?._$setDirectiveConnected?.(false);
currentDirective =
nextDirectiveConstructor === undefined
? undefined
: new nextDirectiveConstructor({
...part,
_$part: part,
_$parent,
_$attributeIndex,
} as PartInfo);
if (_$attributeIndex !== undefined) {
((_$parent as AttributePart).__directives ??= [])[
_$attributeIndex
] = currentDirective;
} else {
(_$parent as ChildPart | Directive).__directive = currentDirective;
}
}
if (currentDirective !== undefined) {
value = currentDirective._resolve((value as DirectiveResult).values);
}
return value;
}
/**

@@ -581,7 +714,15 @@ * An updateable instance of a Template. Holds references to the Parts used to

class TemplateInstance {
__template: Template;
__parts: Array<Part | undefined> = [];
/** @internal */
_$template: Template;
/** @internal */
_parts: Array<Part | undefined> = [];
constructor(template: Template) {
this.__template = template;
/** @internal */
_$parent: Disconnectable;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable> = undefined;
constructor(template: Template, parent: ChildPart) {
this._$template = template;
this._$parent = parent;
}

@@ -591,7 +732,7 @@

// DocumentFragment and we don't want to hold onto it with an instance field.
__clone(options: RenderOptions | undefined) {
_clone(options: RenderOptions | undefined) {
const {
__element: {content},
__parts: parts,
} = this.__template;
_$element: {content},
_parts: parts,
} = this._$template;
const fragment = d.importNode(content, true);

@@ -606,18 +747,26 @@ walker.currentNode = fragment;

while (templatePart !== undefined && node !== null) {
if (nodeIndex === templatePart.__index) {
if (nodeIndex === templatePart._index) {
let part: Part | undefined;
if (templatePart.__type === NODE_PART) {
part = new NodePart(node as HTMLElement, node.nextSibling, options);
} else if (templatePart.__type === ATTRIBUTE_PART) {
part = new templatePart.__constructor(
if (templatePart._type === CHILD_PART) {
part = new ChildPartImpl(
node as HTMLElement,
templatePart.__name,
templatePart.__strings,
node.nextSibling,
this,
options
);
} else if (templatePart._type === ATTRIBUTE_PART) {
part = new templatePart._constructor(
node as HTMLElement,
templatePart._name,
templatePart._strings,
this,
options
);
} else if (templatePart._type === ELEMENT_PART) {
part = new ElementPartImpl(node as HTMLElement, this, options);
}
this.__parts.push(part);
this._parts.push(part);
templatePart = parts[++partIndex];
}
if (templatePart !== undefined && nodeIndex !== templatePart.__index) {
if (templatePart !== undefined && nodeIndex !== templatePart._index) {
node = walker.nextNode();

@@ -630,15 +779,17 @@ nodeIndex++;

__update(values: Array<unknown>) {
_update(values: Array<unknown>) {
let i = 0;
for (const part of this.__parts) {
if (part === undefined) {
i++;
continue;
for (const part of this._parts) {
if (part !== undefined) {
if ((part as AttributePart).strings !== undefined) {
(part as AttributePart)._$setValue(values, part as AttributePart, i);
// The number of values the part consumes is part.strings.length - 1
// since values are in between template spans. We increment i by 1
// later in the loop, so increment it by part.strings.length - 2 here
i += (part as AttributePart).strings!.length - 2;
} else {
part._$setValue(values[i]);
}
}
if ((part as AttributePart).strings !== undefined) {
(part as AttributePart)._setValue(values, i);
i += (part as AttributePart).strings!.length - 1;
} else {
(part as NodePart)._setValue(values[i++]);
}
i++;
}

@@ -651,20 +802,31 @@ }

*/
type AttributePartConstructor = {
new (
element: HTMLElement,
name: string,
strings: ReadonlyArray<string>,
parent: Disconnectable | undefined,
options: RenderOptions | undefined
): AttributePart;
};
type AttributeTemplatePart = {
readonly __type: typeof ATTRIBUTE_PART;
readonly __index: number;
readonly __name: string;
readonly __constructor: typeof AttributePart;
readonly __strings: ReadonlyArray<string>;
readonly _type: typeof ATTRIBUTE_PART;
readonly _index: number;
readonly _name: string;
/** @internal */
readonly _constructor: AttributePartConstructor;
/** @internal */
readonly _strings: ReadonlyArray<string>;
};
type NodeTemplatePart = {
readonly __type: typeof NODE_PART;
readonly __index: number;
readonly _type: typeof CHILD_PART;
readonly _index: number;
};
type ElementTemplatePart = {
readonly __type: typeof ELEMENT_PART;
readonly __index: number;
readonly _type: typeof ELEMENT_PART;
readonly _index: number;
};
type CommentTemplatePart = {
readonly __type: typeof COMMENT_PART;
readonly __index: number;
readonly _type: typeof COMMENT_PART;
readonly _index: number;
};

@@ -684,79 +846,133 @@

export type Part =
| NodePart
| ChildPart
| AttributePart
| PropertyPart
| BooleanAttributePart;
| BooleanAttributePart
| ElementPart
| EventPart;
export class NodePart {
readonly type = NODE_PART;
_value: unknown;
protected __directive?: Directive;
type Interface<T> = {[P in keyof T]: T[P]};
export type ChildPart = Interface<ChildPartImpl>;
class ChildPartImpl {
readonly type = CHILD_PART;
readonly options: RenderOptions | undefined;
_$committedValue: unknown;
/** @internal */
__directive?: Directive;
/** @internal */
_$startNode: ChildNode;
/** @internal */
_$endNode: ChildNode | null;
private _textSanitizer: ValueSanitizer | undefined;
/** @internal */
_$parent: Disconnectable | undefined;
// The following fields will be patched onto ChildParts when required by
// DisconnectableDirective
/** @internal */
_$disconnetableChildren?: Set<Disconnectable> = undefined;
/** @internal */
_$setChildPartConnected?(
isConnected: boolean,
removeFromParent?: boolean,
from?: number
): void;
constructor(
public _startNode: ChildNode,
public _endNode: ChildNode | null,
public options: RenderOptions | undefined
) {}
startNode: ChildNode,
endNode: ChildNode | null,
parent: TemplateInstance | ChildPart | undefined,
options: RenderOptions | undefined
) {
this._$startNode = startNode;
this._$endNode = endNode;
this._$parent = parent;
this.options = options;
if (ENABLE_EXTRA_SECURITY_HOOKS) {
// Explicitly initialize for consistent class shape.
this._textSanitizer = undefined;
}
}
_setValue(value: unknown): void {
// TODO (justinfagnani): when setting a non-directive over a directive,
// we don't yet clear this.__directive.
// See https://github.com/Polymer/lit-html/issues/1286
/**
* Sets the connection state for any `DisconnectableDirectives` contained
* within this part and runs their `disconnectedCallback` or
* `reconnectedCallback`, according to the `isConnected` argument.
* @param isConnected
*/
setConnected(isConnected: boolean) {
this._$setChildPartConnected?.(isConnected);
}
get parentNode(): Node {
return this._$startNode.parentNode!;
}
_$setValue(value: unknown, directiveParent: DirectiveParent = this): void {
value = resolveDirective(this, value, directiveParent);
if (isPrimitive(value)) {
if (value !== this._value) {
this.__commitText(value);
if (value === nothing) {
this._$clear();
this._$committedValue = nothing;
} else if (value !== this._$committedValue && value !== noChange) {
this._commitText(value);
}
} else if ((value as TemplateResult)._$litType$ !== undefined) {
this.__commitTemplateResult(value as TemplateResult);
} else if ((value as DirectiveResult)._$litDirective$ !== undefined) {
this.__commitDirective(value as DirectiveResult);
this._commitTemplateResult(value as TemplateResult);
} else if ((value as Node).nodeType !== undefined) {
this._commitNode(value as Node);
} else if (isIterable(value)) {
this.__commitIterable(value);
} else if (value === nothing) {
this._value = nothing;
this.__clear();
} else if (value !== noChange) {
this._commitIterable(value);
} else {
// Fallback, will render the string representation
this.__commitText(value);
this._commitText(value);
}
}
private __insert<T extends Node>(node: T, ref = this._endNode) {
return this._startNode.parentNode!.insertBefore(node, ref);
private _insert<T extends Node>(node: T, ref = this._$endNode) {
return this._$startNode.parentNode!.insertBefore(node, ref);
}
private __commitDirective(value: DirectiveResult) {
const directive = value._$litDirective$;
if (this.__directive?.constructor !== directive) {
this.__clear();
this.__directive = new directive(this as NodePartInfo);
}
// TODO (justinfagnani): To support nested directives, we'd need to
// resolve the directive result's values. We may want to offer another
// way of composing directives.
this._setValue(this.__directive.update(this, value.values));
}
private _commitNode(value: Node): void {
if (this._value !== value) {
this.__clear();
this._value = this.__insert(value);
if (this._$committedValue !== value) {
this._$clear();
if (
ENABLE_EXTRA_SECURITY_HOOKS &&
sanitizerFactoryInternal !== noopSanitizer
) {
const parentNodeName = this._$startNode.parentNode?.nodeName;
if (parentNodeName === 'STYLE' || parentNodeName === 'SCRIPT') {
this._insert(
new Text(
'/* lit-html will not write ' +
'TemplateResults to scripts and styles */'
)
);
return;
}
}
this._$committedValue = this._insert(value);
}
}
private __commitText(value: unknown): void {
const node = this._startNode.nextSibling;
private _commitText(value: unknown): void {
const node = this._$startNode.nextSibling;
// Make sure undefined and null render as an empty string
// TODO: use `nothing` to clear the node?
value ??= '';
// TODO(justinfagnani): Can we just check if this._value is primitive?
// TODO(justinfagnani): Can we just check if this._$committedValue is primitive?
if (
node !== null &&
node.nodeType === 3 /* Node.TEXT_NODE */ &&
(this._endNode === null
(this._$endNode === null
? node.nextSibling === null
: node === this._endNode.previousSibling)
: node === this._$endNode.previousSibling)
) {
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._textSanitizer === undefined) {
this._textSanitizer = createSanitizer(node, 'data', 'property');
}
value = this._textSanitizer(value);
}
// If we only have a single text node between the markers, we can just

@@ -766,28 +982,46 @@ // set its value, rather than replacing it.

} else {
this._commitNode(new Text(value as string));
if (ENABLE_EXTRA_SECURITY_HOOKS) {
const textNode = document.createTextNode('');
this._commitNode(textNode);
// When setting text content, for security purposes it matters a lot
// what the parent is. For example, <style> and <script> need to be
// handled with care, while <span> does not. So first we need to put a
// text node into the document, then we can sanitize its contentx.
if (this._textSanitizer === undefined) {
this._textSanitizer = createSanitizer(textNode, 'data', 'property');
}
value = this._textSanitizer(value);
textNode.data = value as string;
} else {
this._commitNode(d.createTextNode(value as string));
}
}
this._value = value;
this._$committedValue = value;
}
private __commitTemplateResult(result: TemplateResult): void {
const {strings, values} = result;
let template = templateCache.get(strings);
if (template === undefined) {
templateCache.set(strings, (template = new Template(result)));
}
if (
this._value != null &&
(this._value as TemplateInstance).__template === template
) {
(this._value as TemplateInstance).__update(values);
private _commitTemplateResult(result: TemplateResult): void {
const {values, strings} = result;
const template = this._$getTemplate(strings, result);
if ((this._$committedValue as TemplateInstance)?._$template === template) {
(this._$committedValue as TemplateInstance)._update(values);
} else {
const instance = new TemplateInstance(template!);
const fragment = instance.__clone(this.options);
instance.__update(values);
const instance = new TemplateInstance(template!, this);
const fragment = instance._clone(this.options);
instance._update(values);
this._commitNode(fragment);
this._value = instance;
this._$committedValue = instance;
}
}
private __commitIterable(value: Iterable<unknown>): void {
// Overridden via `litHtmlPlatformSupport` to provide platform support.
/** @internal */
_$getTemplate(strings: TemplateStringsArray, result: TemplateResult) {
let template = templateCache.get(strings);
if (template === undefined) {
templateCache.set(strings, (template = new TemplateImpl(result)));
}
return template;
}
private _commitIterable(value: Iterable<unknown>): void {
// For an Iterable, we create a new InstancePart per item, then set its

@@ -800,8 +1034,8 @@ // value to the item. This is a little bit of overhead for every item in

// If value is an array, then the previous render was of an
// iterable and value will contain the NodeParts from the previous
// iterable and value will contain the ChildParts from the previous
// render. If value is not an array, clear this part and make a new
// array for NodeParts.
if (!isArray(this._value)) {
this._value = [];
this.__clear();
// array for ChildParts.
if (!isArray(this._$committedValue)) {
this._$committedValue = [];
this._$clear();
}

@@ -811,5 +1045,5 @@

// items from a previous render
const itemParts = this._value as NodePart[];
const itemParts = this._$committedValue as ChildPartImpl[];
let partIndex = 0;
let itemPart: NodePart | undefined;
let itemPart: ChildPartImpl | undefined;

@@ -823,5 +1057,6 @@ for (const item of value) {

itemParts.push(
(itemPart = new NodePart(
this.__insert(createMarker()),
this.__insert(createMarker()),
(itemPart = new ChildPartImpl(
this._insert(createMarker()),
this._insert(createMarker()),
this,
this.options

@@ -834,3 +1069,3 @@ ))

}
itemPart._setValue(item);
itemPart._$setValue(item);
partIndex++;

@@ -840,11 +1075,26 @@ }

if (partIndex < itemParts.length) {
// itemParts always have end nodes
this._$clear(itemPart?._$endNode!.nextSibling, partIndex);
// Truncate the parts array so _value reflects the current state
itemParts.length = partIndex;
// itemParts always have end nodes
this.__clear(itemPart?._endNode!.nextSibling);
}
}
__clear(start: ChildNode | null = this._startNode.nextSibling) {
while (start && start !== this._endNode) {
/**
* Removes the nodes contained within this Part from the DOM.
*
* @param start Start node to clear from, for clearing a subset of the part's
* DOM (used when truncating iterables)
* @param from When `start` is specified, the index within the iterable from
* which ChildParts are being removed, used for disconnecting directives in
* those Parts.
*
* @internal
*/
_$clear(
start: ChildNode | null = this._$startNode.nextSibling,
from?: number
) {
this._$setChildPartConnected?.(false, true, from);
while (start && start !== this._$endNode) {
const n = start!.nextSibling;

@@ -857,3 +1107,4 @@ start!.remove();

export class AttributePart {
export type AttributePart = Interface<AttributePartImpl>;
class AttributePartImpl {
readonly type = ATTRIBUTE_PART as

@@ -866,2 +1117,3 @@ | typeof ATTRIBUTE_PART

readonly name: string;
readonly options: RenderOptions | undefined;

@@ -874,5 +1126,19 @@ /**

readonly strings?: ReadonlyArray<string>;
_value: unknown | Array<unknown> = nothing;
private __directives?: Array<Directive>;
/** @internal */
_$committedValue: unknown | Array<unknown> = nothing;
/** @internal */
__directives?: Array<Directive | undefined>;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable> = undefined;
protected _sanitizer: ValueSanitizer | undefined;
/** @internal */
_setDirectiveConnected?: (
directive: Directive | undefined,
isConnected: boolean,
removeFromParent?: boolean
) => void = undefined;
get tagName() {

@@ -886,50 +1152,28 @@ return this.element.tagName;

strings: ReadonlyArray<string>,
_options?: RenderOptions
parent: Disconnectable | undefined,
options: RenderOptions | undefined
) {
this.element = element;
this.name = name;
this._$parent = parent;
this.options = options;
if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') {
this._value = new Array(strings.length - 1).fill(nothing);
this._$committedValue = new Array(strings.length - 1).fill(nothing);
this.strings = strings;
} else {
this._value = nothing;
this._$committedValue = nothing;
}
}
/**
* Normalizes a user-provided value before writing it to the DOM. In the
* near future this will include invoking a directive if the value is
* a DirectiveResult.
*
* @param value the raw input value to normalize
* @param _i the index in the values array this value was read from
*/
__resolveValue(value: unknown, i: number) {
const directiveCtor = (value as DirectiveResult)?._$litDirective$;
if (directiveCtor !== undefined) {
// TODO (justinfagnani): Initialize array to the correct value,
// or check length.
let directive: Directive = (this.__directives ??= [])[i];
if (directive?.constructor !== directiveCtor) {
directive = this.__directives[i] = new directiveCtor(
this as AttributePartInfo
);
}
// TODO (justinfagnani): To support nested directives, we'd need to
// resolve the directive result's values. We may want to offer another
// way of composing directives.
value = directive.update(this, (value as DirectiveResult).values);
if (ENABLE_EXTRA_SECURITY_HOOKS) {
this._sanitizer = undefined;
}
return value ?? '';
}
/**
* Sets the value of this part.
*
* If this part is single-valued, `this.__strings` will be undefined, and the
* Sets the value of this part by resolving the value from possibly multiple
* values and static strings and committing it to the DOM.
* If this part is single-valued, `this._strings` will be undefined, and the
* method will be called with a single value argument. If this part is
* multi-value, `this.__strings` will be defined, and the method is called
* multi-value, `this._strings` will be defined, and the method is called
* with the value array of the part's owning TemplateInstance, and an offset
* into the value array from which the values should be read.
*
* This method is overloaded this way to eliminate short-lived array slices

@@ -940,67 +1184,77 @@ * of the template instance values, and allow a fast-path for single-valued

* @param value The part value, or an array of values for multi-valued parts
* @param from the index to start reading values from. `undefined` for
* @param valueIndex the index to start reading values from. `undefined` for
* single-valued parts
* @param noCommit causes the part to not commit its value to the DOM. Used
* in hydration to prime attribute parts with their first-rendered value,
* but not set the attribute, and in SSR to no-op the DOM operation and
* capture the value for serialization.
*
* @internal
*/
_setValue(value: unknown): void;
_setValue(value: Array<unknown>, from: number): void;
_setValue(value: unknown | Array<unknown>, from?: number) {
_$setValue(
value: unknown | Array<unknown>,
directiveParent: DirectiveParent = this,
valueIndex?: number,
noCommit?: boolean
) {
const strings = this.strings;
// Whether any of the values has changed, for dirty-checking
let change = false;
if (strings === undefined) {
// Single-value binding case
const v = this.__resolveValue(value, 0);
// Only dirty-check primitives and `nothing`:
// `(isPrimitive(v) || v === nothing)` limits the clause to primitives and
// `nothing`. `v === this._value` is the dirty-check.
if (
!((isPrimitive(v) || v === nothing) && v === this._value) &&
v !== noChange
) {
this.__commitValue((this._value = v));
value = resolveDirective(this, value, directiveParent, 0);
change =
!isPrimitive(value) ||
(value !== this._$committedValue && value !== noChange);
if (change) {
this._$committedValue = value;
}
} else {
// Interpolation case
let attributeValue = strings[0];
const values = value as Array<unknown>;
value = strings[0];
// Whether any of the values has changed, for dirty-checking
let change = false;
// Whether any of the values is the `nothing` sentinel. If any are, we
// remove the entire attribute.
let remove = false;
let i, v;
for (i = 0; i < strings.length - 1; i++) {
v = this.__resolveValue((value as Array<unknown>)[from! + i], i);
v = resolveDirective(this, values[valueIndex! + i], directiveParent, i);
if (v === noChange) {
// If the user-provided value is `noChange`, use the previous value
v = (this._value as Array<unknown>)[i];
} else {
remove = remove || v === nothing;
change =
change ||
!(
(isPrimitive(v) || v === nothing) &&
v === (this._value as Array<unknown>)[i]
);
(this._value as Array<unknown>)[i] = v;
v = (this._$committedValue as Array<unknown>)[i];
}
attributeValue +=
(typeof v === 'string' ? v : String(v)) + strings[i + 1];
change ||=
!isPrimitive(v) || v !== (this._$committedValue as Array<unknown>)[i];
if (v === nothing) {
value = nothing;
} else if (value !== nothing) {
value += (v ?? '') + strings[i + 1];
}
// We always record each value, even if one is `nothing`, for future
// change detection.
(this._$committedValue as Array<unknown>)[i] = v;
}
if (change) {
this.__commitValue(remove ? nothing : attributeValue);
}
}
if (change && !noCommit) {
this._commitValue(value);
}
}
/**
* Writes the value to the DOM. An override point for PropertyPart and
* BooleanAttributePart.
*/
__commitValue(value: unknown) {
/** @internal */
_commitValue(value: unknown) {
if (value === nothing) {
this.element.removeAttribute(this.name);
} else {
this.element.setAttribute(this.name, value as string);
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._sanitizer === undefined) {
this._sanitizer = sanitizerFactoryInternal(
this.element,
this.name,
'attribute'
);
}
value = this._sanitizer(value ?? '');
}
this.element.setAttribute(this.name, (value ?? '') as string);
}

@@ -1010,6 +1264,19 @@ }

export class PropertyPart extends AttributePart {
export type PropertyPart = Interface<PropertyPartImpl>;
class PropertyPartImpl extends AttributePartImpl {
readonly type = PROPERTY_PART;
__commitValue(value: unknown) {
/** @internal */
_commitValue(value: unknown) {
if (ENABLE_EXTRA_SECURITY_HOOKS) {
if (this._sanitizer === undefined) {
this._sanitizer = sanitizerFactoryInternal(
this.element,
this.name,
'property'
);
}
value = this._sanitizer(value);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.element as any)[this.name] = value === nothing ? undefined : value;

@@ -1019,6 +1286,8 @@ }

export class BooleanAttributePart extends AttributePart {
export type BooleanAttributePart = Interface<BooleanAttributePartImpl>;
class BooleanAttributePartImpl extends AttributePartImpl {
readonly type = BOOLEAN_ATTRIBUTE_PART;
__commitValue(value: unknown) {
/** @internal */
_commitValue(value: unknown) {
if (value && value !== nothing) {

@@ -1046,15 +1315,17 @@ this.element.setAttribute(this.name, '');

*/
export class EventPart extends AttributePart {
export type EventPart = Interface<EventPartImpl>;
class EventPartImpl extends AttributePartImpl {
readonly type = EVENT_PART;
__eventContext?: unknown;
constructor(...args: ConstructorParameters<typeof AttributePart>) {
super(...args);
this.__eventContext = args[3]?.eventContext;
}
// EventPart does not use the base _$setValue/_resolveValue implementation
// since the dirty checking is more complex
/** @internal */
_$setValue(newListener: unknown, directiveParent: DirectiveParent = this) {
newListener =
resolveDirective(this, newListener, directiveParent, 0) ?? nothing;
if (newListener === noChange) {
return;
}
const oldListener = this._$committedValue;
_setValue(newListener: unknown) {
newListener ??= nothing;
const oldListener = this._value;
// If the new value is nothing or any options change we have to remove the

@@ -1094,12 +1365,12 @@ // part as a listener.

}
this._value = newListener;
this._$committedValue = newListener;
}
handleEvent(event: Event) {
if (typeof this._value === 'function') {
// TODO (justinfagnani): do we need to default to this.__element?
if (typeof this._$committedValue === 'function') {
// TODO (justinfagnani): do we need to default to this._$element?
// It'll always be the same as `e.currentTarget`.
this._value.call(this.__eventContext ?? this.element, event);
this._$committedValue.call(this.options?.host ?? this.element, event);
} else {
(this._value as EventListenerObject).handleEvent(event);
(this._$committedValue as EventListenerObject).handleEvent(event);
}

@@ -1109,5 +1380,83 @@ }

export type ElementPart = Interface<ElementPartImpl>;
class ElementPartImpl {
readonly type = ELEMENT_PART;
/** @internal */
__directive?: Directive;
// This is to ensure that every Part has a _$committedValue
_$committedValue: undefined;
/** @internal */
_$parent: Disconnectable | undefined;
/** @internal */
_$disconnetableChildren?: Set<Disconnectable> = undefined;
/** @internal */
_setDirectiveConnected?: (
directive: Directive | undefined,
isConnected: boolean,
removeFromParent?: boolean
) => void = undefined;
options: RenderOptions | undefined;
constructor(
public element: Element,
parent: Disconnectable,
options: RenderOptions | undefined
) {
this._$parent = parent;
this.options = options;
}
_$setValue(value: unknown): void {
resolveDirective(this, value);
}
}
/**
* END USERS SHOULD NOT RELY ON THIS OBJECT.
*
* Private exports for use by other Lit packages, not intended for use by
* external users.
*
* We currently do not make a mangled rollup build of the lit-ssr code. In order
* to keep a number of (otherwise private) top-level exports mangled in the
* client side code, we export a _$private object containing those members (or
* helper methods for accessing private fields of those members), and then
* re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
* client-side code is being used in `dev` mode or `prod` mode.
*
* @private
*/
export const _$private = {
// Used in lit-ssr
_boundAttributeSuffix: boundAttributeSuffix,
_marker: marker,
_markerMatch: markerMatch,
_HTML_RESULT: HTML_RESULT,
_getTemplateHtml: getTemplateHtml,
// Used in hydrate
_TemplateInstance: TemplateInstance,
_isIterable: isIterable,
_resolveDirective: resolveDirective,
// Used in tests and private-ssr-support
_ChildPart: ChildPartImpl,
_AttributePart: AttributePartImpl as AttributePartConstructor,
_BooleanAttributePart: BooleanAttributePartImpl as AttributePartConstructor,
_EventPart: EventPartImpl as AttributePartConstructor,
_PropertyPart: PropertyPartImpl as AttributePartConstructor,
};
// Apply polyfills if available
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any)['litHtmlPlatformSupport']?.(TemplateImpl, ChildPartImpl);
// IMPORTANT: do not change the property name or the assignment expression.
// This line will be used in regexes to search for lit-html usage.
// TODO(justinfagnani): inject version number at build time
((globalThis as any)['litHtmlVersions'] ??= []).push('2.0.0-pre.3');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
((globalThis as any)['litHtmlVersions'] ??= []).push('2.0.0-pre.4');

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

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

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

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