Skip to content
This repository has been archived by the owner on Aug 25, 2022. It is now read-only.

Commit

Permalink
add directive types (#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
popeindustries committed Oct 9, 2019
1 parent 9e05378 commit d2ab47d
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 140 deletions.
104 changes: 82 additions & 22 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,71 +11,71 @@ declare module '@popeindustries/lit-html-server' {
destroy: (err: Error) => void;
}

const defaultTemplateProcessor: DefaultTemplateProcessor;
const defaultTemplateResultProcessor: DefaultTemplateResultProcessor;
export const defaultTemplateProcessor: DefaultTemplateProcessor;
export const defaultTemplateResultProcessor: DefaultTemplateResultProcessor;

/**
* Define new directive for "fn".
* The passed function should be a factory function,
* and must return a function that will eventually be called with a Part instance
*/
function directive(fn: Directive): Directive;
export function directive(fn: Directive): Directive;

/**
* Determine if "part" is an AttributePart
*/
function isAttributePart(part: Part): boolean;
export function isAttributePart(part: Part): boolean;

/**
* Determine if "part" is a NodePart
*/
function isNodePart(part: Part): boolean;
export function isNodePart(part: Part): boolean;

/**
* Determine whether "result" is a TemplateResult
*/
function isTemplateResult(result: TemplateResult): boolean;
export function isTemplateResult(result: TemplateResult): boolean;

/**
* A value for strings that signals a Part to clear its content
*/
const nothingString: string;
const templateCache: Map<TemplateStringsArray, Template>;
export const nothingString: string;
export const templateCache: Map<TemplateStringsArray, Template>;

/**
* A prefix value for strings that should not be escaped
*/
const unsafePrefixString: string;
export const unsafePrefixString: string;

/**
* Interprets a template literal as an HTML template that can be
* rendered as a Readable stream or String
*/
function html(strings: TemplateStringsArray, ...values: Array<unknown>): TemplateResult;
function svg(strings: TemplateStringsArray, ...values: Array<unknown>): TemplateResult;
export function html(strings: TemplateStringsArray, ...values: Array<unknown>): TemplateResult;
export function svg(strings: TemplateStringsArray, ...values: Array<unknown>): TemplateResult;

/**
* Render a template result to a string resolving Promise.
* *Note* that TemplateResults are single use, and can only be rendered once.
*/
function renderToString(result: TemplateResult): Promise<string>;
export function renderToString(result: TemplateResult): Promise<string>;

/**
* Render a template result to a Readable stream
* *Note* that TemplateResults are single use, and can only be rendered once.
*/
function renderToStream(result: TemplateResult): import('stream').Readable;
export function renderToStream(result: TemplateResult): import('stream').Readable;

/**
* Render a template result to a Buffer resolving Promise.
* *Note* that TemplateResults are single use, and can only be rendered once.
*/
function renderToBuffer(result: TemplateResult): Promise<Buffer>;
export function renderToBuffer(result: TemplateResult): Promise<Buffer>;

/**
* Base class interface for Node/Attribute parts
*/
class Part {
export class Part {
/**
* Store the current value.
* Used by directives to temporarily transfer value
Expand All @@ -87,7 +87,7 @@ declare module '@popeindustries/lit-html-server' {
/**
* A dynamic template part for text nodes
*/
class NodePart extends Part {
export class NodePart extends Part {
/**
* Retrieve resolved value given passed "value"
*/
Expand All @@ -98,7 +98,7 @@ declare module '@popeindustries/lit-html-server' {
* A dynamic template part for attributes.
* Unlike text nodes, attributes may contain multiple strings and parts.
*/
class AttributePart extends Part {
export class AttributePart extends Part {
/*
* @param { Array<any> } values
* @returns { Buffer|Promise<Buffer> }
Expand All @@ -110,7 +110,7 @@ declare module '@popeindustries/lit-html-server' {
* A dynamic template part for boolean attributes.
* Boolean attributes are prefixed with "?"
*/
class BooleanAttributePart extends AttributePart {
export class BooleanAttributePart extends AttributePart {
/**
* Retrieve resolved string Buffer from passed "values".
*/
Expand All @@ -121,7 +121,7 @@ declare module '@popeindustries/lit-html-server' {
* A dynamic template part for property attributes.
* Property attributes are prefixed with "."
*/
class PropertyAttributePart extends AttributePart {
export class PropertyAttributePart extends AttributePart {
/**
* Retrieve resolved string Buffer from passed "values".
* Properties have no server-side representation,
Expand All @@ -134,7 +134,7 @@ declare module '@popeindustries/lit-html-server' {
* A dynamic template part for event attributes.
* Event attributes are prefixed with "@"
*/
class EventAttributePart extends AttributePart {
export class EventAttributePart extends AttributePart {
/**
* Retrieve resolved string Buffer from passed "values".
* Event bindings have no server-side representation,
Expand All @@ -148,7 +148,7 @@ declare module '@popeindustries/lit-html-server' {
* Exposes factory functions for generating Part instances to use for
* resolving a template's dynamic values.
*/
class DefaultTemplateProcessor {
export class DefaultTemplateProcessor {
/**
* Create part instance for dynamic attribute values
*/
Expand All @@ -166,7 +166,7 @@ declare module '@popeindustries/lit-html-server' {
*
* @implements TemplateResultProcessor
*/
class DefaultTemplateResultProcessor {
export class DefaultTemplateResultProcessor {
/**
* Process "stack" and push chunks to "renderer"
*/
Expand Down Expand Up @@ -222,3 +222,63 @@ declare module '@popeindustries/lit-html-server' {
_prepare(strings: Array<TemplateStringsArray>, processor: TemplateProcessor): void;
}
}

declare module '@popeindustres/lit-html-server/directives/async-append.js' {
import { Part } from '@popeindustries/lit-html-server';

export const asyncAppend: (value: AsyncIterable<unknown>) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/cache.js' {
import { Part } from '@popeindustries/lit-html-server';

export const cache: (value: unknown) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/class-map.js' {
import { Part } from '@popeindustries/lit-html-server';

export const classMap: (classInfo: {
[name: string]: string | boolean | number;
}) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/guard.js' {
import { Part } from '@popeindustries/lit-html-server';

export const guard: (value: unknown, fn: () => unknown) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/if-defined.js' {
import { Part } from '@popeindustries/lit-html-server';

export const ifDefined: (value: unknown) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/repeat.js' {
import { Part } from '@popeindustries/lit-html-server';

export const repeat: (
items: Array<unknown>,
keyFnOrTemplate: (item: unknown, index: number) => unknown,
template?: (item: unknown, index: number) => unknown
) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/style-map.js' {
import { Part } from '@popeindustries/lit-html-server';

export const styleMap: (styleInfo: { [name: string]: string }) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/unsafe-html.js' {
import { Part } from '@popeindustries/lit-html-server';

export const unsafeHTML: (value: unknown) => (part: Part) => void;
}

declare module '@popeindustres/lit-html-server/directives/until.js' {
import { Part } from '@popeindustries/lit-html-server';

export const until: (...args: Array<unknown>) => (part: Part) => void;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"prepublishOnly": "npm run build",
"test": "npm run build && NODE_ENV=test mocha \"test/*-test.js\" --reporter spec --bail --timeout 2000 --require esm"
},
"types": "index.d.ts",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
Expand Down
21 changes: 8 additions & 13 deletions src/directives/async-append.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
/**
* @typedef NodePart { import('../parts.js').NodePart }
* @typedef Part { import('../parts.js').Part }
*/
import { directive, isNodePart } from '../index.js';

export const asyncAppend = directive(asyncAppendDirective);

/**
* Render items of an AsyncIterable
*
* @param { AsyncIterableIterator } value
* @returns { (part: NodePart) => void }
* @type { (value: AsyncIterable<unknown>) => (part: Part) => void }
*/
function asyncAppendDirective(value) {
return function(part) {
if (!isNodePart(part)) {
throw Error('The `asyncAppend` directive can only be used in text nodes');
}
part.setValue(value);
};
}
export const asyncAppend = directive((value) => (part) => {
if (!isNodePart(part)) {
throw Error('The `asyncAppend` directive can only be used in text nodes');
}
part.setValue(value);
});
21 changes: 8 additions & 13 deletions src/directives/cache.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
/**
* @typedef NodePart { import('../parts.js').NodePart }
* @typedef Part { import('../parts.js').Part }
*/
import { directive, isNodePart } from '../index.js';

export const cache = directive(cacheDirective);

/**
* Enables fast switching between multiple templates by caching previous results.
* Not possible/desireable to cache between server-side requests, so this is a no-op.
*
* @param { unknown } value
* @returns { (part: NodePart) => void }
* @type { (value: unknown) => (part: Part) => void }
*/
function cacheDirective(value) {
return function(part) {
if (!isNodePart(part)) {
throw Error('The `cache` directive can only be used in text nodes');
}
part.setValue(value);
};
}
export const cache = directive((value) => (part) => {
if (!isNodePart(part)) {
throw Error('The `cache` directive can only be used in text nodes');
}
part.setValue(value);
});
32 changes: 15 additions & 17 deletions src/directives/class-map.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
/**
* @typedef Part { import('../parts.js').Part }
*/
import { directive, isAttributePart } from '../index.js';

export const classMap = directive(classMapDirective);

/**
* Applies CSS classes, where'classInfo' keys are added as class names if values are truthy.
* Only applies to 'class' attribute.
*
* @param { object } classInfo
* @returns { (part: AttributePart) => void }
* @type { (classInfo: { [name: string]: string | boolean | number }) => (part: Part) => void }
*/
function classMapDirective(classInfo) {
return function(part) {
if (!isAttributePart(part) || part.name !== 'class') {
throw Error('The `classMap` directive can only be used in the `class` attribute');
}
export const classMap = directive((classInfo) => (part) => {
if (!isAttributePart(part) || part.name !== 'class') {
throw Error('The `classMap` directive can only be used in the `class` attribute');
}

let value = '';
let value = '';

for (const key in classInfo) {
if (classInfo[key]) {
value += `${value.length ? ' ' : ''}${key}`;
}
for (const key in classInfo) {
if (classInfo[key]) {
value += `${value.length ? ' ' : ''}${key}`;
}
}

part.setValue(value);
};
}
part.setValue(value);
});
17 changes: 7 additions & 10 deletions src/directives/guard.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
/**
* @typedef Part { import('../parts.js').Part }
*/
import { directive } from '../index.js';

export const guard = directive(guardDirective);

/**
* Guard against re-render.
* Not possible to compare against previous render in a server context,
* so this is a no-op.
*
* @param { unknown } value
* @param { () => unknown } fn
* @returns { (part: NodePart) => void }
* @type { (value: unknown, fn: () => unknown) => (part: Part) => void }
*/
function guardDirective(value, fn) {
return function(part) {
part.setValue(fn());
};
}
export const guard = directive((value, fn) => (part) => {
part.setValue(fn());
});
22 changes: 10 additions & 12 deletions src/directives/if-defined.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
/**
* @typedef Part { import('../parts.js').Part }
*/
import { directive, isAttributePart, nothingString } from '../index.js';

export const ifDefined = directive(ifDefinedDirective);

/**
* Sets the attribute if 'value' is defined,
* removes the attribute if undefined.
*
* @param { unknown } value
* @returns { (part: AttributePart) => void }
* @type { (value: unknown) => (part: Part) => void }
*/
function ifDefinedDirective(value) {
return function(part) {
if (value === undefined && isAttributePart(part)) {
return part.setValue(nothingString);
}
part.setValue(value);
};
}
export const ifDefined = directive((value) => (part) => {
if (value === undefined && isAttributePart(part)) {
return part.setValue(nothingString);
}
part.setValue(value);
});

0 comments on commit d2ab47d

Please sign in to comment.