From 455daf3210e42a7e98bc012fd75db658a5935424 Mon Sep 17 00:00:00 2001 From: Kevin Schaaf Date: Wed, 18 Jan 2023 19:17:09 -0800 Subject: [PATCH] Initial draft of cem-* elements for rendering manifest data --- package-lock.json | 19 +- packages/site-client/package.json | 5 +- .../src/pages/element/wco-element-page.ts | 41 +-- .../src/shared/cem/cem-class-declaration.ts | 292 ++++++++++++++++++ .../shared/cem/cem-function-declaration.ts | 28 ++ .../src/shared/cem/cem-js-module.ts | 132 ++++++++ .../src/shared/cem/cem-mixin-declaration.ts | 29 ++ .../src/shared/cem/cem-package.ts.ts | 42 +++ .../src/shared/cem/cem-reexport.ts | 32 ++ .../src/shared/cem/cem-reference.ts | 38 +++ .../site-client/src/shared/cem/cem-type.ts | 42 +++ .../shared/cem/cem-variable-declaration.ts | 42 +++ packages/site-client/src/shared/cem/common.ts | 94 ++++++ 13 files changed, 807 insertions(+), 29 deletions(-) create mode 100644 packages/site-client/src/shared/cem/cem-class-declaration.ts create mode 100644 packages/site-client/src/shared/cem/cem-function-declaration.ts create mode 100644 packages/site-client/src/shared/cem/cem-js-module.ts create mode 100644 packages/site-client/src/shared/cem/cem-mixin-declaration.ts create mode 100644 packages/site-client/src/shared/cem/cem-package.ts.ts create mode 100644 packages/site-client/src/shared/cem/cem-reexport.ts create mode 100644 packages/site-client/src/shared/cem/cem-reference.ts create mode 100644 packages/site-client/src/shared/cem/cem-type.ts create mode 100644 packages/site-client/src/shared/cem/cem-variable-declaration.ts create mode 100644 packages/site-client/src/shared/cem/common.ts 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

- - -

Methods

- -
+ +
+

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` + + + + + + + + + + + ${params.map(renderParameter)} + +
NameTypeDescriptionDefault
+ ` + )} + + + ${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

+ + + + + + + + + + + ${m.map((p) => renderClassField(p as cem.ClassField))} + +
NameTypeDescriptionDefault
` + )} + ${whenDefined( + this.declaration.members?.filter( + (m) => m.kind === 'method' && m.privacy !== 'private' + ), + (m) => html`

Methods

+ + + + + + + + + + + ${m.map((p) => renderClassMethod(p as cem.ClassMethod))} + +
NameDescriptionParametersReturn
` + )} + ${whenDefined( + (this.declaration as cem.CustomElementDeclaration).slots, + (m) => html`

Slots

+ + + + + + + + + ${m.map((p) => renderSlot(p as cem.ClassMethod))} + +
NameDescription
` + )} + ${whenDefined( + (this.declaration as cem.CustomElementDeclaration).cssProperties, + (m) => html`

CSS Custom Properties

+ + + + + + + + + + + ${m.map((p) => renderCssProperty(p as cem.ClassMethod))} + +
NameSyntaxDescriptionDefault
` + )} + ${whenDefined( + (this.declaration as cem.CustomElementDeclaration).cssParts, + (m) => html`

CSS Parts

+ + + + + + + + + ${m.map((p) => renderCssPart(p as cem.ClassMethod))} + +
NameDescription
` + )} + ${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)}`;