diff --git a/package-lock.json b/package-lock.json
index 4f63cf38..1eeaeb44 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23805,9 +23805,12 @@
"version": "0.0.0",
"license": "Apache-2.0",
"dependencies": {
+ "@types/marked": "^4.0.8",
"@webcomponents/catalog-api": "^0.0.0",
+ "custom-elements-manifest": "^2.0.0",
"lit": "^2.6.0",
- "lit-analyzer": "^1.2.1"
+ "lit-analyzer": "^1.2.1",
+ "marked": "^4.2.5"
}
},
"packages/site-content": {
@@ -23818,7 +23821,8 @@
"@11ty/eleventy": "^1.0.2",
"@11ty/eleventy-navigation": "^0.3.5",
"@webcomponents/internal-site-client": "^0.0.0",
- "@webcomponents/internal-site-server": "^0.0.0"
+ "@webcomponents/internal-site-server": "^0.0.0",
+ "@webcomponents/internal-site-templates": "^0.0.0"
}
},
"packages/site-server": {
@@ -23839,6 +23843,7 @@
"@types/marked": "^4.0.8",
"@web/dev-server": "^0.1.34",
"@webcomponents/internal-site-content": "^0.0.0",
+ "@webcomponents/internal-site-templates": "^0.0.0",
"google-auth-library": "^8.7.0",
"koa": "^2.13.4",
"koa-conditional-get": "^3.0.0",
@@ -23851,6 +23856,7 @@
}
},
"packages/site-templates": {
+ "name": "@webcomponents/internal-site-templates",
"version": "0.0.0",
"license": "Apache-2.0"
}
@@ -27648,9 +27654,12 @@
"@webcomponents/internal-site-client": {
"version": "file:packages/site-client",
"requires": {
+ "@types/marked": "^4.0.8",
"@webcomponents/catalog-api": "^0.0.0",
+ "custom-elements-manifest": "^2.0.0",
"lit": "^2.6.0",
- "lit-analyzer": "^1.2.1"
+ "lit-analyzer": "^1.2.1",
+ "marked": "^4.2.5"
}
},
"@webcomponents/internal-site-content": {
@@ -27659,7 +27668,8 @@
"@11ty/eleventy": "^1.0.2",
"@11ty/eleventy-navigation": "^0.3.5",
"@webcomponents/internal-site-client": "^0.0.0",
- "@webcomponents/internal-site-server": "^0.0.0"
+ "@webcomponents/internal-site-server": "^0.0.0",
+ "@webcomponents/internal-site-templates": "^0.0.0"
}
},
"@webcomponents/internal-site-server": {
@@ -27678,6 +27688,7 @@
"@types/marked": "^4.0.8",
"@web/dev-server": "^0.1.34",
"@webcomponents/internal-site-content": "^0.0.0",
+ "@webcomponents/internal-site-templates": "^0.0.0",
"google-auth-library": "^8.7.0",
"koa": "^2.13.4",
"koa-conditional-get": "^3.0.0",
diff --git a/packages/site-client/package.json b/packages/site-client/package.json
index 014cf002..2981faea 100644
--- a/packages/site-client/package.json
+++ b/packages/site-client/package.json
@@ -86,8 +86,11 @@
}
},
"dependencies": {
+ "@types/marked": "^4.0.8",
"@webcomponents/catalog-api": "^0.0.0",
+ "custom-elements-manifest": "^2.0.0",
"lit": "^2.6.0",
- "lit-analyzer": "^1.2.1"
+ "lit-analyzer": "^1.2.1",
+ "marked": "^4.2.5"
}
}
diff --git a/packages/site-client/src/pages/element/wco-element-page.ts b/packages/site-client/src/pages/element/wco-element-page.ts
index 4ea26fc9..6abb84a2 100644
--- a/packages/site-client/src/pages/element/wco-element-page.ts
+++ b/packages/site-client/src/pages/element/wco-element-page.ts
@@ -6,7 +6,6 @@
import {html, css} from 'lit';
import {customElement, property} from 'lit/decorators.js';
-import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import type {Package, Reference} from 'custom-elements-manifest/schema.js';
import {
@@ -16,6 +15,7 @@ import {
normalizeModulePath,
} from '@webcomponents/custom-elements-manifest-tools';
import {WCOPage} from '../../shared/wco-page.js';
+import '../../shared/cem/cem-class-declaration.js';
export interface ElementData {
packageName: string;
@@ -80,7 +80,6 @@ export class WCOElementPage extends WCOPage {
#content {
grid-area: c;
- padding: 1em;
}
`,
];
@@ -88,6 +87,13 @@ export class WCOElementPage extends WCOPage {
@property({attribute: false})
elementData?: ElementData;
+ // TODO(kschaaf) Use context to provide reference resolver
+ // @provide({context: cemReferenceResolver})
+ // referenceResolver = (ref: Reference) => {
+ // const {package: pkg, module, name} = ref;
+ // return `#${pkg}${module === undefined ? '' : `/${module}`}:${name}`;
+ // };
+
renderContent() {
if (this.elementData === undefined) {
return html`
No element to display
`;
@@ -98,7 +104,6 @@ export class WCOElementPage extends WCOPage {
declarationReference,
customElementExport,
manifest,
- elementDescriptionHtml,
} = this.elementData;
const ceExportRef = parseReferenceString(customElementExport);
const declarationRef = parseReferenceString(declarationReference);
@@ -117,9 +122,6 @@ export class WCOElementPage extends WCOPage {
`;
}
- const fields = declaration.members?.filter((m) => m.kind === 'field');
- const methods = declaration.members?.filter((m) => m.kind === 'method');
-
// TODO (justinfagnani): We need a better way to make a summary from a
// description, that's possibly markdown, word, and sentence boundary
// aware.
@@ -146,24 +148,15 @@ export class WCOElementPage extends WCOPage {
Install
npm install ${packageName}
-
-
${unsafeHTML(elementDescriptionHtml)}
-
-
Usage
-
- import '${getElementImportSpecifier(packageName, ceExportRef)}';
-
-
-
Fields
-
- ${fields?.map((f) => html`- ${f.name}: ${f.description}
`)}
-
-
-
Methods
-
- ${methods?.map((m) => html`- ${m.name}: ${m.description}
`)}
-
-
+
+
+
Usage
+
import '${getElementImportSpecifier(
+ packageName,
+ ceExportRef
+ )}';
+
+
`;
}
}
diff --git a/packages/site-client/src/shared/cem/cem-class-declaration.ts b/packages/site-client/src/shared/cem/cem-class-declaration.ts
new file mode 100644
index 00000000..81a1a5f0
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-class-declaration.ts
@@ -0,0 +1,292 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {
+ styles,
+ whenDefined,
+ renderDeclarationInfo,
+ markdown,
+} from './common.js';
+
+import './cem-type.js';
+import './cem-reference.js';
+
+const renderMemberName = (
+ name: string,
+ deprecated: boolean | string | undefined,
+ privacy?: cem.Privacy | undefined,
+ statik?: boolean | undefined,
+ optional?: boolean,
+ rest?: boolean
+) => {
+ return html`
+
+ ${whenDefined(
+ deprecated,
+ (d) =>
+ html`deprecated
+ `
+ )}
+ ${privacy !== undefined && privacy !== 'public'
+ ? privacy + ' '
+ : ''}${statik ? 'static ' : ''}${rest ? '...' : ''}
+ ${name}
+ ${optional ? html` (optional)` : ''}
+ `;
+};
+
+const renderClassField = (field: cem.ClassField) => {
+ const {
+ name,
+ privacy,
+ static: statik,
+ deprecated,
+ description,
+ summary,
+ type,
+ default: def,
+ // source,
+ // inheritedFrom,
+ } = field;
+ return html`
+ ${renderMemberName(name, deprecated, privacy, statik)} |
+ ${whenDefined(type, (t) => html``)} |
+ ${markdown(summary)}${markdown(description)} |
+ ${whenDefined(def, (d) => html`${d} `)} |
+
`;
+};
+
+const renderParameter = (param: cem.Parameter) => {
+ const {
+ name,
+ description,
+ summary,
+ type,
+ default: def,
+ rest,
+ optional,
+ deprecated,
+ } = param;
+ return html`
+
+
+ ${renderMemberName(
+ name,
+ deprecated,
+ 'public',
+ false,
+ optional,
+ rest
+ )}
+ |
+
+ ${whenDefined(type, (t) => html``)}
+ |
+ ${markdown(summary)}${markdown(description)} |
+ ${whenDefined(def, (d) => html`${d} `)} |
+
+ `;
+};
+
+const renderClassMethod = (method: cem.ClassMethod) => {
+ const {
+ name,
+ privacy,
+ static: statik,
+ deprecated,
+ description,
+ summary,
+ return: ret,
+ parameters,
+ // TODO(kschaaf) Not implemented in CEM yet
+ // source,
+ // inheritedFrom,
+ } = method;
+ return html`
+ ${renderMemberName(name, deprecated, privacy, statik)} |
+ ${markdown(summary)}${markdown(description)} |
+
+ ${whenDefined(
+ parameters,
+ (params: cem.Parameter[]) =>
+ html`
+
+
+
+ Name |
+ Type |
+ Description |
+ Default |
+
+
+
+ ${params.map(renderParameter)}
+
+
+ `
+ )}
+ |
+
+ ${whenDefined(
+ ret?.type,
+ (t) =>
+ html`Type: ${markdown(
+ ret?.summary
+ )}${markdown(ret?.description)}`
+ )}
+ |
+
`;
+};
+
+const renderSlot = (slot: cem.Slot) => {
+ return html`
+ ${renderMemberName(slot.name, slot.deprecated)}} |
+ ${markdown(slot.summary)}${markdown(slot.description)} |
+
`;
+};
+
+const renderCssProperty = (prop: cem.CssCustomProperty) => {
+ return html`
+ ${renderMemberName(prop.name, prop.deprecated)}} |
+ ${whenDefined(prop.syntax)} |
+ ${markdown(prop.summary)}${markdown(prop.description)} |
+ ${whenDefined(prop.default)} |
+
`;
+};
+
+const renderCssPart = (part: cem.CssPart) => {
+ return html`
+ ${renderMemberName(part.name, part.deprecated)}} |
+ ${markdown(part.summary)}${markdown(part.description)} |
+
`;
+};
+
+@customElement('cem-class-declaration')
+export class CemClassDeclaration extends LitElement {
+ static styles = styles;
+ @property()
+ declaration!: cem.ClassDeclaration;
+ @property()
+ exportName?: string;
+ render() {
+ return html`
+ ${renderDeclarationInfo(this.declaration, this.exportName)}
+
+ ${whenDefined(
+ this.declaration.members?.filter(
+ (m) => m.kind === 'field' && m.privacy !== 'private'
+ ),
+ (m) => html` Fields
+
+
+
+ Name |
+ Type |
+ Description |
+ Default |
+
+
+
+ ${m.map((p) => renderClassField(p as cem.ClassField))}
+
+
`
+ )}
+ ${whenDefined(
+ this.declaration.members?.filter(
+ (m) => m.kind === 'method' && m.privacy !== 'private'
+ ),
+ (m) => html` Methods
+
+
+
+ Name |
+ Description |
+ Parameters |
+ Return |
+
+
+
+ ${m.map((p) => renderClassMethod(p as cem.ClassMethod))}
+
+
`
+ )}
+ ${whenDefined(
+ (this.declaration as cem.CustomElementDeclaration).slots,
+ (m) => html` Slots
+
+
+
+ Name |
+ Description |
+
+
+
+ ${m.map((p) => renderSlot(p as cem.ClassMethod))}
+
+
`
+ )}
+ ${whenDefined(
+ (this.declaration as cem.CustomElementDeclaration).cssProperties,
+ (m) => html` CSS Custom Properties
+
+
+
+ Name |
+ Syntax |
+ Description |
+ Default |
+
+
+
+ ${m.map((p) => renderCssProperty(p as cem.ClassMethod))}
+
+
`
+ )}
+ ${whenDefined(
+ (this.declaration as cem.CustomElementDeclaration).cssParts,
+ (m) => html` CSS Parts
+
+
+
+ Name |
+ Description |
+
+
+
+ ${m.map((p) => renderCssPart(p as cem.ClassMethod))}
+
+
`
+ )}
+ ${whenDefined(
+ this.declaration.superclass,
+ (s) => html`Super Class
+ `
+ )}
+ ${whenDefined(
+ this.declaration.mixins,
+ (mixins) => html`Mixins
+ ${mixins.map(
+ (m) => html``
+ )}`
+ )}
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-class-declaration': CemClassDeclaration;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-function-declaration.ts b/packages/site-client/src/shared/cem/cem-function-declaration.ts
new file mode 100644
index 00000000..9713af30
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-function-declaration.ts
@@ -0,0 +1,28 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {styles, renderDeclarationInfo} from './common.js';
+
+@customElement('cem-function-declaration')
+export class CemFunctionDeclaration extends LitElement {
+ static styles = styles;
+ @property()
+ declaration!: cem.FunctionDeclaration;
+ @property()
+ exportName?: string;
+ render() {
+ return renderDeclarationInfo(this.declaration, this.exportName);
+ //TODO(kschaaf) Render the rest of the stuff
+ }
+}
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-function-declaration': CemFunctionDeclaration;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-js-module.ts b/packages/site-client/src/shared/cem/cem-js-module.ts
new file mode 100644
index 00000000..e7e4e73d
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-js-module.ts
@@ -0,0 +1,132 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {styles, whenDefined, markdown} from './common.js';
+
+import './cem-variable-declaration.js';
+import './cem-class-declaration.js';
+import './cem-mixin-declaration.js';
+import './cem-function-declaration.js';
+import './cem-reexport.js';
+
+@customElement('cem-js-module')
+export class CemJsModule extends LitElement {
+ static styles = styles;
+ @property()
+ module!: cem.JavaScriptModule;
+ render() {
+ const ceExports = this.module.exports?.filter(
+ (e) => e.kind === 'custom-element-definition'
+ );
+ const jsExports = this.module.exports?.filter(
+ (e) =>
+ e.kind === 'js' &&
+ !ceExports?.find(
+ (ce) =>
+ e.declaration.package === undefined &&
+ ce.declaration.name === e.declaration.name
+ )
+ );
+ return html`
+ Module: ${this.module.path}
+ ${whenDefined(
+ this.module.summary,
+ (s) => html`
+ Summary
+ ${markdown(s)}
+ `
+ )}
+ ${whenDefined(
+ this.module.description,
+ (s) => html`
+ Description
+ ${markdown(s)}
+ `
+ )}
+ ${whenDefined(
+ ceExports,
+ (e) => html`
+ Custom Elements
+ ${e.map((exp) =>
+ renderExport(
+ exp as cem.JavaScriptExport,
+ this.module.declarations ?? []
+ )
+ )}
+ `
+ )}
+ ${whenDefined(
+ jsExports,
+ (e) => html`
+ JavaScript Exports
+ ${e.map((exp) =>
+ renderExport(
+ exp as cem.JavaScriptExport,
+ this.module.declarations ?? []
+ )
+ )}
+ `
+ )}
+ `;
+ }
+}
+
+const renderDeclaration = (
+ d: cem.Declaration | undefined,
+ exportName?: string
+) => {
+ if (d === undefined) {
+ return html`Error: Declaration not found`;
+ }
+ switch (d.kind) {
+ case 'function':
+ return html``;
+ case 'class':
+ return html``;
+ case 'variable':
+ return html``;
+ case 'mixin':
+ return html``;
+ default:
+ return html`Unsupported declartion kind`;
+ }
+};
+
+const renderExport = (
+ exp: cem.JavaScriptExport,
+ declarations: cem.Declaration[]
+) => {
+ return exp.declaration.package === undefined
+ ? renderDeclaration(
+ declarations.find((d) => d.name === exp.declaration.name),
+ exp.name
+ )
+ : html``;
+};
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-js-module': CemJsModule;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-mixin-declaration.ts b/packages/site-client/src/shared/cem/cem-mixin-declaration.ts
new file mode 100644
index 00000000..535360d2
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-mixin-declaration.ts
@@ -0,0 +1,29 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {styles, renderDeclarationInfo} from './common.js';
+
+@customElement('cem-mixin-declaration')
+export class CemMixinDeclaration extends LitElement {
+ static styles = styles;
+ @property()
+ declaration!: cem.MixinDeclaration;
+ @property()
+ exportName?: string;
+ render() {
+ return renderDeclarationInfo(this.declaration, this.exportName);
+ //TODO(kschaaf) Render the rest of the stuff
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-mixin-declaration': CemMixinDeclaration;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-package.ts.ts b/packages/site-client/src/shared/cem/cem-package.ts.ts
new file mode 100644
index 00000000..fd09a721
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-package.ts.ts
@@ -0,0 +1,42 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {styles, markdown} from './common.js';
+
+import './cem-js-module.js';
+
+@customElement('cem-package')
+export class CemPackage extends LitElement {
+ static styles = styles;
+ @property()
+ name!: string;
+ @property()
+ package!: cem.Package;
+ render() {
+ return html`
+ Package: ${this.name}
+ ${markdown(this.package.readme)} ${this.package.modules.map(module)}
+ `;
+ }
+}
+
+const module = (m: cem.Module) => {
+ switch (m.kind) {
+ case 'javascript-module':
+ return html``;
+ default:
+ return html`Not implemented`;
+ }
+};
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-package': CemPackage;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-reexport.ts b/packages/site-client/src/shared/cem/cem-reexport.ts
new file mode 100644
index 00000000..21821ec8
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-reexport.ts
@@ -0,0 +1,32 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {styles} from './common.js';
+
+import './cem-reference.js';
+
+@customElement('cem-reexport')
+export class CemReexport extends LitElement {
+ static styles = styles;
+ @property()
+ name!: string;
+ @property()
+ reference!: cem.Reference;
+ render() {
+ return html` ${this.name}
+ Re-export of
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-reexport': CemReexport;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-reference.ts b/packages/site-client/src/shared/cem/cem-reference.ts
new file mode 100644
index 00000000..e331e47e
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-reference.ts
@@ -0,0 +1,38 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import {ifDefined} from 'lit/directives/if-defined.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {CemReferenceResolver, defaultReferenceResolver} from './common.js';
+
+const specifierFromReference = (ref: cem.Reference) => {
+ const {package: pkg, module} = ref;
+ return `${pkg}${module === undefined ? '' : `/${module}`}`;
+};
+
+@customElement('cem-reference')
+export class CemReference extends LitElement {
+ // TODO(kschaaf) Eventually use context to provide this
+ // @consume({context: cemReferenceResolver, subscribe: true})
+ @property()
+ resolveReference: CemReferenceResolver = defaultReferenceResolver;
+ @property()
+ reference!: cem.Reference;
+ render() {
+ return html`${this.reference.name}
+ from ${specifierFromReference(this.reference)}
`;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-reference': CemReference;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-type.ts b/packages/site-client/src/shared/cem/cem-type.ts
new file mode 100644
index 00000000..e8feccd2
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-type.ts
@@ -0,0 +1,42 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import {ifDefined} from 'lit/directives/if-defined.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {CemReferenceResolver, defaultReferenceResolver} from './common.js';
+
+@customElement('cem-type')
+export class CemType extends LitElement {
+ // TODO(kschaaf) Eventually use context to provide this
+ // @consume({context: cemReferenceResolver, subscribe: true})
+ @property()
+ resolveReference: CemReferenceResolver = defaultReferenceResolver;
+ @property()
+ type!: cem.Type;
+ render() {
+ const {text, references: refs = []} = this.type;
+ return html`${[
+ ...refs.map(
+ (r, idx) =>
+ html`${text.slice(refs[idx - 1]?.end ?? 0, r.start)}${text.slice(r.start, r.end)}`
+ ),
+ text.slice(refs[refs.length - 1]?.end ?? 0),
+ ]}
`;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-type': CemType;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/cem-variable-declaration.ts b/packages/site-client/src/shared/cem/cem-variable-declaration.ts
new file mode 100644
index 00000000..5599c028
--- /dev/null
+++ b/packages/site-client/src/shared/cem/cem-variable-declaration.ts
@@ -0,0 +1,42 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {html, LitElement} from 'lit';
+import {customElement, property} from 'lit/decorators.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {
+ styles,
+ whenDefined,
+ markdown,
+ renderDeclarationInfo,
+} from './common.js';
+
+import './cem-type.js';
+
+@customElement('cem-variable-declaration')
+export class CemVariableDeclaration extends LitElement {
+ static styles = styles;
+ @property()
+ declaration!: cem.VariableDeclaration;
+ @property()
+ exportName?: string;
+ render() {
+ return html`
+ ${renderDeclarationInfo(this.declaration, this.exportName)}
+ ${markdown(this.declaration.summary)}
+ ${markdown(this.declaration.description)}
+ ${whenDefined(
+ this.declaration.type,
+ (t: cem.Type) => html`Type: `
+ )}
+ `;
+ }
+}
+declare global {
+ interface HTMLElementTagNameMap {
+ 'cem-variable-declaration': CemVariableDeclaration;
+ }
+}
diff --git a/packages/site-client/src/shared/cem/common.ts b/packages/site-client/src/shared/cem/common.ts
new file mode 100644
index 00000000..a338ed4f
--- /dev/null
+++ b/packages/site-client/src/shared/cem/common.ts
@@ -0,0 +1,94 @@
+/**
+ * @license
+ * Copyright 2022 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {css, html, nothing} from 'lit';
+import {unsafeHTML} from 'lit/directives/unsafe-html.js';
+import type * as cem from 'custom-elements-manifest/schema.js';
+import {marked} from 'marked';
+
+export const styles = css`
+ :host {
+ display: block;
+ padding: 0 0 15px 15px;
+ margin-bottom: 15px;
+ background-color: rgba(0, 0, 0, 0.03);
+ border: 1px solid #aaa;
+ border-radius: 6px;
+ }
+ code {
+ display: inline-block;
+ padding: 4px;
+ background-color: rgba(0, 0, 0, 0.03);
+ border-radius: 4px;
+ }
+ table {
+ border-collapse: separate;
+ }
+ th {
+ text-align: start;
+ font-size: 0.8em;
+ font-weight: 600;
+ border-bottom: 1px solid #aaa;
+ background-color: rgba(0, 0, 0, 0.04);
+ padding: 4px;
+ }
+ td {
+ vertical-align: top;
+ padding: 4px;
+ }
+ tr:nth-child(even) {
+ background-color: rgba(0, 0, 0, 0.03);
+ }
+ h1.title,
+ h2.title,
+ h3.title,
+ h4.title {
+ background-color: #fff;
+ border-bottom: 1px solid #aaa;
+ margin: 0 0 15px -15px;
+ padding: 15px 0 5px 15px;
+ border-radius: 10px 10px 0 0;
+ }
+`;
+
+export type CemReferenceResolver = (reference: cem.Reference) => string;
+
+export const defaultReferenceResolver = (ref: cem.Reference) => {
+ const {package: pkg, module, name} = ref;
+ return `#${pkg}${module === undefined ? '' : `/${module}`}:${name}`;
+};
+
+export const whenDefined = <
+ V,
+ T extends undefined | ((v: NonNullable) => unknown)
+>(
+ v: V,
+ transform?: T
+) =>
+ v == null ||
+ (Array.isArray(v) && v.length === 0) ||
+ (typeof v === 'string' && v.length === 0) ||
+ v === false
+ ? nothing
+ : transform !== undefined
+ ? transform(v)
+ : v;
+
+export const markdown = (content: string | undefined) =>
+ content !== undefined ? unsafeHTML(marked(content)) : nothing;
+
+export const renderDeclarationInfo = (
+ declaration: cem.Declaration,
+ exportName?: string
+) =>
+ html`
+ ${exportName !== undefined ? exportName : declaration.name}
+ (${declaration.kind}${exportName !== undefined &&
+ exportName !== declaration.name
+ ? ` ${declaration.name}`
+ : ''})
+
+ ${markdown(declaration.summary)} ${markdown(declaration.description)}`;