From 1a16a55d605825b5629598b0e165ac4b091773a8 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 11:11:40 -0800 Subject: [PATCH 01/29] adding template-renderer feature and spec files --- .../fast-ssr/src/template-renderer/template-renderer.spec.ts | 0 .../fast-ssr/src/template-renderer/template-renderer.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts create mode 100644 packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts new file mode 100644 index 00000000000..e69de29bb2d From 4473f686f8b3f57360f7032a40e6b03fa37f2122 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 11:31:56 -0800 Subject: [PATCH 02/29] adding DOM emission configuration to TemplateRenderer --- .../template-renderer.spec.ts | 19 +++++++++++++++++++ .../template-renderer/template-renderer.ts | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts index e69de29bb2d..d3bb84a1a13 100644 --- a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts +++ b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts @@ -0,0 +1,19 @@ +import { test, expect } from "@playwright/test"; +import { TemplateRenderer } from "./template-renderer.js"; +import { template } from "@babel/core"; + +test.describe("TemplateRenderer", () => { + test.describe("should have an initial configuration", () => { + test("that emits to shadow DOM", () => { + const instance = new TemplateRenderer(); + expect(instance.componentDOMEmissionMode).toBe("shadow") + }); + }); + + test.describe("should allow configuration", () => { + test("that emits to light DOM", () => { + const instance = new TemplateRenderer({componentDOMEmissionMode: "light"}); + expect(instance.componentDOMEmissionMode).toBe("light") + }) + }) +}); diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts index e69de29bb2d..4be27467e5b 100644 --- a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts +++ b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts @@ -0,0 +1,19 @@ +export type ComponentDOMEmissionMode = "shadow" | "light"; +export interface TemplateRendererConfiguration { + /** + * Controls whether the template renderer should emit component template code to the component's shadow DOM or to its light DOM. + */ + componentDOMEmissionMode: ComponentDOMEmissionMode; +} + +export class TemplateRenderer implements Readonly { + /** + * {@inheritDoc TemplateRendererConfiguration.componentDOMEmissionMode} + */ + public readonly componentDOMEmissionMode: ComponentDOMEmissionMode = "shadow"; + constructor(config?: TemplateRendererConfiguration) { + if (config) { + Object.assign(this, config); + } + } +} From 5acb026492b2a9d369403096b2645d437bbe188c Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 11:43:41 -0800 Subject: [PATCH 03/29] adding render function to TemplateRenderer --- .../src/template-renderer/template-renderer.spec.ts | 4 ++-- .../src/template-renderer/template-renderer.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts index d3bb84a1a13..2d2ccc77056 100644 --- a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts +++ b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.spec.ts @@ -14,6 +14,6 @@ test.describe("TemplateRenderer", () => { test("that emits to light DOM", () => { const instance = new TemplateRenderer({componentDOMEmissionMode: "light"}); expect(instance.componentDOMEmissionMode).toBe("light") - }) - }) + }); + }); }); diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts index 4be27467e5b..2893a7cf965 100644 --- a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts +++ b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts @@ -1,3 +1,6 @@ +import { ViewTemplate } from "@microsoft/fast-element"; +import { RenderInfo } from "@lit-labs/ssr"; + export type ComponentDOMEmissionMode = "shadow" | "light"; export interface TemplateRendererConfiguration { /** @@ -16,4 +19,12 @@ export class TemplateRenderer implements Readonly Object.assign(this, config); } } + + public *render( + template: ViewTemplate, + renderInfo: RenderInfo, + source?: unknown + ): IterableIterator { + yield ""; + } } From ee977b6a1bf9bd337392190de6f101d25cb64def Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 11:45:23 -0800 Subject: [PATCH 04/29] adding method description to TemplateRenderer.render --- .../fast-ssr/src/template-renderer/template-renderer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts index 2893a7cf965..cc866d94f7c 100644 --- a/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts +++ b/packages/web-components/fast-ssr/src/template-renderer/template-renderer.ts @@ -20,6 +20,12 @@ export class TemplateRenderer implements Readonly } } + /** + * + * @param template - The template to render. + * @param renderInfo - Information about the rendering context. + * @param source - Any source data to render the template and evaluate bindings with. + */ public *render( template: ViewTemplate, renderInfo: RenderInfo, From 06500fd22f7d9130267d73c0ece338fa77799435 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 14:42:34 -0800 Subject: [PATCH 05/29] add template parser files and entry functions --- .../fast-ssr/src/element-renderer/op-codes.ts | 9 +++++++++ .../element-renderer/template-parser.spec.ts | 0 .../src/element-renderer/template-parser.ts | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 packages/web-components/fast-ssr/src/element-renderer/op-codes.ts create mode 100644 packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts create mode 100644 packages/web-components/fast-ssr/src/element-renderer/template-parser.ts diff --git a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts new file mode 100644 index 00000000000..ca0390efeb8 --- /dev/null +++ b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts @@ -0,0 +1,9 @@ +/** + * Operation to output static text + */ +export type TextOp = { + type: "text"; + value: string; +}; + +export type Op = TextOp; diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts new file mode 100644 index 00000000000..95dd5d45bed --- /dev/null +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -0,0 +1,18 @@ +import { ViewTemplate } from "@microsoft/fast-element"; +import { Op } from "./op-codes.js"; + +const opCache: Map = new Map(); + +/** + * Parses a template into a set of operation instructions + * @param template - The template to parse + */ +export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { + if (opCache.has(template)) { + return opCache.get(template)!; + } + + const ops: Op[] = []; + + return ops; +} From dc9a30e90f7ef47b42770759b18038667d276885 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 15:14:15 -0800 Subject: [PATCH 06/29] parseTemplateToOpCodes should throw when used with an HTMLTemplateElement template --- .../element-renderer/template-parser.spec.ts | 18 ++++++++++++++++++ .../src/element-renderer/template-parser.ts | 7 +++++++ 2 files changed, 25 insertions(+) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts index e69de29bb2d..e87efdda43f 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts @@ -0,0 +1,18 @@ + +import "@lit-labs/ssr/lib/install-global-dom-shim.js"; +import { test, expect } from "@playwright/test"; +import { parseTemplateToOpCodes} from "./template-parser.js"; +import { ViewTemplate } from "@microsoft/fast-element" + +test.describe("parseTemplateToOpCodes", () => { + test("should throw when invoked with a ViewTemplate with a HTMLTemplateElement template", () => { + expect(() => { + parseTemplateToOpCodes(new ViewTemplate(document.createElement("template"), [])); + }).toThrow(); + }); + test("should not throw when invoked with a ViewTemplate with a string template", () => { + expect(() => { + parseTemplateToOpCodes(new ViewTemplate("", [])); + }).not.toThrow(); + }); +}) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 95dd5d45bed..8dba3363fa0 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -13,6 +13,13 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { } const ops: Op[] = []; + const { html } = template; + + if (typeof html !== "string") { + throw new Error( + "@microsoft/fast-ssr does not support rendering a ViewTemplate with an HTMLTemplateElement html source." + ); + } return ops; } From deadcb608dc8c99b04101a83215f7849a0067dc2 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 15:16:00 -0800 Subject: [PATCH 07/29] change script structure to allow breakpoints to percist in files from build to build --- packages/web-components/fast-ssr/package.json | 4 +++- .../fast-ssr/src/element-renderer/template-parser.ts | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/web-components/fast-ssr/package.json b/packages/web-components/fast-ssr/package.json index a01f2139ae4..85e98cecf84 100644 --- a/packages/web-components/fast-ssr/package.json +++ b/packages/web-components/fast-ssr/package.json @@ -15,7 +15,9 @@ "url": "https://github.com/Microsoft/fast/issues/new/choose" }, "scripts": { - "build": "tsc -b --clean src && tsc -b src", + "clean": "tsc -b --clean src", + "build": "tsc -b src", + "prepare": "yarn run clean && yarn run build", "build-server": "tsc -b server", "eslint": "eslint . --ext .ts", "eslint:fix": "eslint . --ext .ts --fix", diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 8dba3363fa0..cac9003be05 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -8,11 +8,12 @@ const opCache: Map = new Map(); * @param template - The template to parse */ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { - if (opCache.has(template)) { - return opCache.get(template)!; + let ops: Op[] | undefined = opCache.get(template); + if (ops !== undefined) { + return ops; } - const ops: Op[] = []; + ops = []; const { html } = template; if (typeof html !== "string") { From 23acadfde4db20728ba5c17b13054bfef9f6a534 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 15:20:56 -0800 Subject: [PATCH 08/29] adding parse5 HTML parser --- packages/web-components/fast-ssr/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web-components/fast-ssr/package.json b/packages/web-components/fast-ssr/package.json index 85e98cecf84..e650709bc65 100644 --- a/packages/web-components/fast-ssr/package.json +++ b/packages/web-components/fast-ssr/package.json @@ -36,6 +36,7 @@ "dependencies": { "@lit-labs/ssr": "^1.0.0-rc.2", "@microsoft/fast-element": "^1.5.0", + "parse5": "^6.0.1", "tslib": "^1.11.1" }, "devDependencies": { From a88624a1c8379d8a1cc2f9fc3e2bffa6ff2535c4 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 15:57:17 -0800 Subject: [PATCH 09/29] generate AST from ViewTemplate --- .../fast-ssr/src/element-renderer/template-parser.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index cac9003be05..ec4293ef0b4 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -1,4 +1,5 @@ import { ViewTemplate } from "@microsoft/fast-element"; +import { parseFragment } from "parse5"; import { Op } from "./op-codes.js"; const opCache: Map = new Map(); @@ -13,7 +14,6 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { return ops; } - ops = []; const { html } = template; if (typeof html !== "string") { @@ -22,5 +22,13 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { ); } + ops = []; + const ast = parseFragment(html, { sourceCodeLocationInfo: true }); + + if (!("nodeName" in ast)) { + // I'm not sure when exactly this is encountered but the type system seems to say it's possible. + throw new Error(`Error parsing template:\n${template}`); + } + return ops; } From 03d9c19337ac215ac67f212421ea343fcb00ac38 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 16:20:59 -0800 Subject: [PATCH 10/29] implement AST traverser --- .../src/element-renderer/template-parser.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index ec4293ef0b4..7d7e8a0894e 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -1,9 +1,39 @@ +/** + * This code is largely a fork of lit's rendering implementation: https://github.com/lit/lit/blob/main/packages/labs/ssr/src/lib/render-lit-html.ts + * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. + */ import { ViewTemplate } from "@microsoft/fast-element"; -import { parseFragment } from "parse5"; +import { DefaultTreeNode, DefaultTreeParentNode, parseFragment } from "parse5"; import { Op } from "./op-codes.js"; const opCache: Map = new Map(); +interface Visitor { + visit?: (node: DefaultTreeNode) => void; + leave?: (node: DefaultTreeNode) => void; +} + +/** + * Traverses a tree of nodes depth-first, invoking callbacks from visitor for each node as it goes. + * @param node - the node to traverse + * @param visitor - callbacks to be invoked during node traversal + */ +function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visitor) { + if (visitor.visit) { + visitor.visit(node); + } + if ("childNodes" in node) { + const { childNodes } = node; + for (const child of childNodes) { + traverse(child, visitor); + } + } + + if (visitor.leave) { + visitor.leave(node); + } +} + /** * Parses a template into a set of operation instructions * @param template - The template to parse From 144acecddd3b1e3010d1bddc57cb9d6fada560b2 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 17 Feb 2022 16:40:31 -0800 Subject: [PATCH 11/29] adding node type checks --- .../src/element-renderer/template-parser.ts | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 7d7e8a0894e..079eafadd00 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -3,7 +3,15 @@ * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. */ import { ViewTemplate } from "@microsoft/fast-element"; -import { DefaultTreeNode, DefaultTreeParentNode, parseFragment } from "parse5"; +import { + DefaultTreeCommentNode, + DefaultTreeDocumentFragment, + DefaultTreeElement, + DefaultTreeNode, + DefaultTreeParentNode, + DefaultTreeTextNode, + parseFragment, +} from "parse5"; import { Op } from "./op-codes.js"; const opCache: Map = new Map(); @@ -34,6 +42,38 @@ function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visito } } +/** + * Test if a node is a comment node. + * @param node - the node to test + */ +function isCommentNode(node: DefaultTreeNode): node is DefaultTreeCommentNode { + return node.nodeName === "#comment"; +} + +/** + * Test if a node is a document fragment node. + * @param node - the node to test + */ +function isDocumentFragment(node: DefaultTreeNode): node is DefaultTreeDocumentFragment { + return node.nodeName === "#document-fragment"; +} + +/** + * Test if a node is a text node. + * @param node - the node to test + */ +function isTextNode(node: DefaultTreeNode): node is DefaultTreeTextNode { + return node.nodeName === "#text"; +} + +/** + * Test if a node is an element node + * @param node - the node to test + */ +function isElementNode(node: DefaultTreeNode): node is DefaultTreeElement { + return (node as DefaultTreeElement).tagName !== undefined; +} + /** * Parses a template into a set of operation instructions * @param template - The template to parse @@ -60,5 +100,10 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { throw new Error(`Error parsing template:\n${template}`); } + traverse(ast, { + visit(node) {}, + leave(node) {}, + }); + return ops; } From a1c36ab7ceec68b5f1b370774dc50fa00ad12081 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Tue, 22 Feb 2022 14:49:03 -0800 Subject: [PATCH 12/29] implement parser class that acts as a node visitor for node traversal --- .../fast-ssr/src/element-renderer/op-codes.ts | 21 ++++- .../src/element-renderer/template-parser.ts | 88 ++++++++++++++++--- 2 files changed, 96 insertions(+), 13 deletions(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts index ca0390efeb8..15485275dd6 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts @@ -1,9 +1,24 @@ /** - * Operation to output static text + * Allows fast identification of operation types + */ +export enum OpType { + text, + customElementClose, +} + +/** + * Operation to emit static text */ export type TextOp = { - type: "text"; + type: OpType.text; value: string; }; -export type Op = TextOp; +/** + * Operation to close a custom element + */ +export type CustomElementCloseOp = { + type: OpType.customElementClose; +}; + +export type Op = TextOp | CustomElementCloseOp; diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 079eafadd00..c9f7397ea86 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -12,24 +12,29 @@ import { DefaultTreeTextNode, parseFragment, } from "parse5"; -import { Op } from "./op-codes.js"; +import { Op, OpType } from "./op-codes.js"; const opCache: Map = new Map(); interface Visitor { visit?: (node: DefaultTreeNode) => void; leave?: (node: DefaultTreeNode) => void; + complete?: () => void; } +// Will be 0 when starting and ending traversal. +let counter = 0; /** * Traverses a tree of nodes depth-first, invoking callbacks from visitor for each node as it goes. * @param node - the node to traverse * @param visitor - callbacks to be invoked during node traversal */ function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visitor) { + counter++; if (visitor.visit) { visitor.visit(node); } + if ("childNodes" in node) { const { childNodes } = node; for (const child of childNodes) { @@ -40,6 +45,12 @@ function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visito if (visitor.leave) { visitor.leave(node); } + + counter--; + + if (counter === 0 && visitor.complete) { + visitor.complete(); + } } /** @@ -74,14 +85,71 @@ function isElementNode(node: DefaultTreeNode): node is DefaultTreeElement { return (node as DefaultTreeElement).tagName !== undefined; } +class TemplateParser implements Visitor { + private lastOffset: number | undefined = 0; + private get lastOp() { + return this.opCodes[this.opCodes.length - 1]; + } + + constructor(private template: string) {} + + public readonly opCodes: Op[] = []; + public visit(node: DefaultTreeNode): void {} + public leave(node: DefaultTreeNode): void {} + public complete() {} + + /** + * Flushes a string value to op codes + * @param value - The value to flush + */ + private flush(value: string): void { + const last = this.lastOp; + if (last?.type === OpType.text) { + last.value += value; + } else { + this.opCodes.push({ type: OpType.text, value }); + } + } + + private flushTo(offset?: number) { + if (this.lastOffset === undefined) { + throw new Error( + `Cannot flush template content from a last offset that is ${typeof this + .lastOffset}.` + ); + } + + const prev = this.lastOffset; + this.lastOffset = offset; + this.flush(this.template.substring(prev, offset)); + } + + private skipTo(offset: number) { + if (this.lastOffset === undefined) { + throw new Error( + `Cannot skip to offset '${offset}' when lastOffset is ${typeof this + .lastOffset}` + ); + } + if (offset < this.lastOffset) { + throw new Error(`offset must be greater than lastOffset. + offset: ${offset} + lastOffset: ${this.lastOffset} + `); + } + + this.lastOffset = offset; + } +} + /** * Parses a template into a set of operation instructions * @param template - The template to parse */ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { - let ops: Op[] | undefined = opCache.get(template); - if (ops !== undefined) { - return ops; + const cached: Op[] | undefined = opCache.get(template); + if (cached !== undefined) { + return cached; } const { html } = template; @@ -92,7 +160,6 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { ); } - ops = []; const ast = parseFragment(html, { sourceCodeLocationInfo: true }); if (!("nodeName" in ast)) { @@ -100,10 +167,11 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { throw new Error(`Error parsing template:\n${template}`); } - traverse(ast, { - visit(node) {}, - leave(node) {}, - }); + const ops: Op[] = []; + const visitor = new TemplateParser(html); + opCache.set(template, visitor.opCodes); + + traverse(ast, visitor); - return ops; + return visitor.opCodes; } From 347fa092ebde63a09818f426a97dbf7700418906 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Tue, 22 Feb 2022 15:16:49 -0800 Subject: [PATCH 13/29] adding flushTo method to TemplateParser --- .../src/element-renderer/template-parser.ts | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index c9f7397ea86..4217b7686c3 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -111,6 +111,10 @@ class TemplateParser implements Visitor { } } + /** + * Flush template content from lastIndex to provided offset + * @param offset - the offset to flush to + */ private flushTo(offset?: number) { if (this.lastOffset === undefined) { throw new Error( @@ -123,23 +127,6 @@ class TemplateParser implements Visitor { this.lastOffset = offset; this.flush(this.template.substring(prev, offset)); } - - private skipTo(offset: number) { - if (this.lastOffset === undefined) { - throw new Error( - `Cannot skip to offset '${offset}' when lastOffset is ${typeof this - .lastOffset}` - ); - } - if (offset < this.lastOffset) { - throw new Error(`offset must be greater than lastOffset. - offset: ${offset} - lastOffset: ${this.lastOffset} - `); - } - - this.lastOffset = offset; - } } /** From 6ed2d38ac18eddc8f011ea005a0a42bf51a14601 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Tue, 22 Feb 2022 15:20:28 -0800 Subject: [PATCH 14/29] implement completion method --- .../fast-ssr/src/element-renderer/template-parser.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 4217b7686c3..13d6f881659 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -96,7 +96,9 @@ class TemplateParser implements Visitor { public readonly opCodes: Op[] = []; public visit(node: DefaultTreeNode): void {} public leave(node: DefaultTreeNode): void {} - public complete() {} + public complete() { + this.flushTo(); + } /** * Flushes a string value to op codes From 496e13e067fe8a0f846d68d15318d3e625d599c7 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Tue, 22 Feb 2022 15:47:43 -0800 Subject: [PATCH 15/29] writing a few test fixtures for pure HTML templates --- .../src/element-renderer/template-parser.spec.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts index e87efdda43f..dbd52afe46b 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts @@ -2,7 +2,8 @@ import "@lit-labs/ssr/lib/install-global-dom-shim.js"; import { test, expect } from "@playwright/test"; import { parseTemplateToOpCodes} from "./template-parser.js"; -import { ViewTemplate } from "@microsoft/fast-element" +import { ViewTemplate, html } from "@microsoft/fast-element" +import { Op, OpType } from "./op-codes.js"; test.describe("parseTemplateToOpCodes", () => { test("should throw when invoked with a ViewTemplate with a HTMLTemplateElement template", () => { @@ -15,4 +16,15 @@ test.describe("parseTemplateToOpCodes", () => { parseTemplateToOpCodes(new ViewTemplate("", [])); }).not.toThrow(); }); + + const fixtures: {input: ViewTemplate, result: Op[]}[] = [ + {input: html`

Hello world

`, result: [{type: OpType.text, value: "

Hello world

"}]}, + {input: html``, result: [{type: OpType.text, value: ""}]} + ]; + + fixtures.forEach(({ input, result}, index) => { + test(`should parse template to op codes for fixture ${index}`, () => { + expect(parseTemplateToOpCodes(input)).toEqual(result) + }) + }) }) From 22180fb2ecc75f493524a6603b73db1b892b8f6b Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Wed, 23 Feb 2022 12:35:15 -0800 Subject: [PATCH 16/29] add directive type and parsing test --- .../fast-element/docs/api-report.md | 9 +- .../fast-ssr/src/element-renderer/marker.ts | 18 + .../fast-ssr/src/element-renderer/op-codes.ts | 13 +- .../element-renderer/template-parser.spec.ts | 16 +- .../src/element-renderer/template-parser.ts | 87 ++- yarn.lock | 541 ++++++------------ 6 files changed, 311 insertions(+), 373 deletions(-) create mode 100644 packages/web-components/fast-ssr/src/element-renderer/marker.ts diff --git a/packages/web-components/fast-element/docs/api-report.md b/packages/web-components/fast-element/docs/api-report.md index e38a9a06d73..4913a43816d 100644 --- a/packages/web-components/fast-element/docs/api-report.md +++ b/packages/web-components/fast-element/docs/api-report.md @@ -49,7 +49,7 @@ export class AttributeDefinition implements Accessor { onAttributeChangedCallback(element: HTMLElement, value: any): void; readonly Owner: Function; setValue(source: HTMLElement, newValue: any): void; - } +} // @public export type AttributeMode = "reflect" | "boolean" | "fromView"; @@ -467,14 +467,14 @@ export class RepeatBehavior implements Behavior, Subscriber { // @internal (undocumented) handleChange(source: any, args: Splice[]): void; unbind(): void; - } +} // @public export class RepeatDirective extends HTMLDirective { constructor(itemsBinding: Binding, templateBinding: Binding, options: RepeatOptions); createBehavior(targets: ViewBehaviorTargets): RepeatBehavior; createPlaceholder: (index: number) => string; - } +} // @public export interface RepeatOptions { @@ -617,7 +617,7 @@ export class ViewTemplate impl readonly directives: ReadonlyArray; readonly html: string | HTMLTemplateElement; render(source: TSource, host: Node, hostBindingTarget?: Element): HTMLView; - } +} // @public export function volatile(target: {}, name: string | Accessor, descriptor: PropertyDescriptor): PropertyDescriptor; @@ -625,7 +625,6 @@ export function volatile(target: {}, name: string | Accessor, descriptor: Proper // @public export function when(binding: Binding, templateOrTemplateBinding: SyntheticViewTemplate | Binding): CaptureType; - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/web-components/fast-ssr/src/element-renderer/marker.ts b/packages/web-components/fast-ssr/src/element-renderer/marker.ts new file mode 100644 index 00000000000..4aa22f1061a --- /dev/null +++ b/packages/web-components/fast-ssr/src/element-renderer/marker.ts @@ -0,0 +1,18 @@ +import { marker } from "@microsoft/fast-element"; +import { DefaultTreeCommentNode } from "parse5"; + +const blockMarker = new RegExp(`${marker}:\\d+`); +export function isMarkerComment(node: DefaultTreeCommentNode): boolean { + return blockMarker.test(node.data); +} + +const interpolationMarker = new RegExp(`${marker}\\{(?\\d+)\\}${marker}`); +export function isInterpolationMarker(node: { value: string }): boolean { + return interpolationMarker.test(node.value); +} + +export function extractInterpolationMarkerId(node: { value: string }): number | null { + const id = interpolationMarker.exec(node.value)?.groups?.id; + + return id === undefined ? null : parseInt(id, 10); +} diff --git a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts index 15485275dd6..dfc3175aabe 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts @@ -1,9 +1,12 @@ +import { AspectedHTMLDirective } from "@microsoft/fast-element"; + /** * Allows fast identification of operation types */ export enum OpType { text, customElementClose, + directive, } /** @@ -21,4 +24,12 @@ export type CustomElementCloseOp = { type: OpType.customElementClose; }; -export type Op = TextOp | CustomElementCloseOp; +/** + * Operation to emit static text + */ +export type DirectiveOp = { + type: OpType.directive; + directive: AspectedHTMLDirective; +}; + +export type Op = TextOp | CustomElementCloseOp | DirectiveOp; diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts index dbd52afe46b..7200c34ce77 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts @@ -17,12 +17,22 @@ test.describe("parseTemplateToOpCodes", () => { }).not.toThrow(); }); - const fixtures: {input: ViewTemplate, result: Op[]}[] = [ + interface Fixture { + input: ViewTemplate, + result: Op[]; + } + + const fixtures: ( (()=> Fixture) | Fixture )[] = [ {input: html`

Hello world

`, result: [{type: OpType.text, value: "

Hello world

"}]}, - {input: html``, result: [{type: OpType.text, value: ""}]} + {input: html``, result: [{type: OpType.text, value: ""}]}, + () => { + const input = html`${() => "hello world"}`; + return { input, result: [{ type: OpType.directive, directive: input.directives[0]}] } as Fixture + } ]; - fixtures.forEach(({ input, result}, index) => { + fixtures.forEach((fixture, index) => { + const { input, result } = typeof fixture === "function" ? fixture() : fixture; test(`should parse template to op codes for fixture ${index}`, () => { expect(parseTemplateToOpCodes(input)).toEqual(result) }) diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts index 13d6f881659..c2db2268627 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts @@ -2,7 +2,12 @@ * This code is largely a fork of lit's rendering implementation: https://github.com/lit/lit/blob/main/packages/labs/ssr/src/lib/render-lit-html.ts * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. */ -import { ViewTemplate } from "@microsoft/fast-element"; +import { + ViewTemplate, + AspectedHTMLDirective, + HTMLDirective, + DOM, +} from "@microsoft/fast-element"; import { DefaultTreeCommentNode, DefaultTreeDocumentFragment, @@ -11,8 +16,14 @@ import { DefaultTreeParentNode, DefaultTreeTextNode, parseFragment, + CommentNode, } from "parse5"; import { Op, OpType } from "./op-codes.js"; +import { + isInterpolationMarker, + isMarkerComment, + extractInterpolationMarkerId, +} from "./marker.js"; const opCache: Map = new Map(); @@ -91,10 +102,30 @@ class TemplateParser implements Visitor { return this.opCodes[this.opCodes.length - 1]; } - constructor(private template: string) {} + constructor(private template: string, private directives: readonly HTMLDirective[]) {} public readonly opCodes: Op[] = []; - public visit(node: DefaultTreeNode): void {} + public visit(node: DefaultTreeNode): void { + if (this.isInterpolationMarkerNode(node) || this.isCommentMarkerNode(node)) { + this.flushTo(node.sourceCodeLocation.startOffset); + + // TODO: clean this up when new APIs from fast-element get integrated. + const directive = this.directives[ + this.isInterpolationMarkerNode(node) + ? extractInterpolationMarkerId(node)! + : DOM.extractDirectiveIndexFromMarker((node as unknown) as Comment) + ]; + if (directive instanceof AspectedHTMLDirective) { + this.opCodes.push({ type: OpType.directive, directive }); + } else { + throw new Error( + `Unexpected directive type encountered. It is a ${directive}.` + ); + } + this.skipTo(node.sourceCodeLocation.endOffset); + } + } + public leave(node: DefaultTreeNode): void {} public complete() { this.flushTo(); @@ -127,7 +158,53 @@ class TemplateParser implements Visitor { const prev = this.lastOffset; this.lastOffset = offset; - this.flush(this.template.substring(prev, offset)); + const value = this.template.substring(prev, offset); + + if (value !== "") { + this.flush(value); + } + } + + private skipTo(offset: number) { + if (this.lastOffset === undefined) { + throw new Error("Could not skip from an undefined offset"); + } + if (offset < this.lastOffset) { + throw new Error(`offset must be greater than lastOffset. + offset: ${offset} + lastOffset: ${this.lastOffset} + `); + } + + this.lastOffset = offset; + } + + /** + * Tests if a node is an interpolated FAST marker + * @param node - the node to test + */ + private isInterpolationMarkerNode( + node: DefaultTreeNode + ): node is Required { + return ( + isTextNode(node) && + node.sourceCodeLocation !== undefined && + isInterpolationMarker(node) + ); + } + + /** + * Tests if a node is a FAST comment marker + * @param node - the node to test + */ + private isCommentMarkerNode( + node: DefaultTreeNode + ): node is Required { + return ( + isCommentNode(node) && + node.sourceCodeLocation !== undefined && + isMarkerComment(node) + ); } } @@ -157,7 +234,7 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { } const ops: Op[] = []; - const visitor = new TemplateParser(html); + const visitor = new TemplateParser(html, template.directives); opCache.set(template, visitor.opCodes); traverse(ast, visitor); diff --git a/yarn.lock b/yarn.lock index cc5bb93bb27..4e29b39abe8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3258,6 +3258,15 @@ js-yaml "~3.13.1" resolve "~1.17.0" +"@microsoft/api-extractor-model@7.15.3": + version "7.15.3" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.15.3.tgz#cf76deeeb2733d974da678f530c2dbaceb18a065" + integrity sha512-NkSjolmSI7NGvbdz0Y7kjQfdpD+j9E5CwXTxEyjDqxd10MI7GXV8DnAsQ57GFJcgHKgTjf2aUnYfMJ9w3aMicw== + dependencies: + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "~0.15.2" + "@rushstack/node-core-library" "3.45.0" + "@microsoft/api-extractor-model@7.8.0": version "7.8.0" resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.8.0.tgz#5f532998f01109f23d57b422803bbdf5ad655d80" @@ -3274,6 +3283,24 @@ "@microsoft/tsdoc" "0.12.19" "@rushstack/node-core-library" "3.24.1" +"@microsoft/api-extractor@7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.19.4.tgz#95d43d410a1dfb28a02062c4693bcb9c52afe9eb" + integrity sha512-iehC6YA3DGJvxTUaK7HUtQmP6hAQU07+Q/OR8TG4dVR6KpqCi9UPEVk8AgCvQkiK+6FbVEFQTx0qLuYk4EeuHg== + dependencies: + "@microsoft/api-extractor-model" "7.15.3" + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "~0.15.2" + "@rushstack/node-core-library" "3.45.0" + "@rushstack/rig-package" "0.3.7" + "@rushstack/ts-command-line" "4.10.6" + colors "~1.2.1" + lodash "~4.17.15" + resolve "~1.17.0" + semver "~7.3.0" + source-map "~0.6.1" + typescript "~4.5.2" + "@microsoft/api-extractor@7.8.1": version "7.8.1" resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.8.1.tgz#29b473ef85273b6b28974f89f1ccd46329297bc3" @@ -3446,6 +3473,16 @@ jju "~1.4.0" resolve "~1.19.0" +"@microsoft/tsdoc-config@~0.15.2": + version "0.15.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz#eb353c93f3b62ab74bdc9ab6f4a82bcf80140f14" + integrity sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA== + dependencies: + "@microsoft/tsdoc" "0.13.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + "@microsoft/tsdoc@0.12.19": version "0.12.19" resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.19.tgz#2173ccb92469aaf62031fa9499d21b16d07f9b57" @@ -3456,6 +3493,11 @@ resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz#30728e34ebc90351dd3aff4e18d038eed2c3e098" integrity sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg== +"@microsoft/tsdoc@0.13.2": + version "0.13.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26" + integrity sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -3879,6 +3921,39 @@ timsort "~0.3.0" z-schema "~3.18.3" +"@rushstack/node-core-library@3.45.0": + version "3.45.0" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.45.0.tgz#8c86b39271b6d84260b1e70db87e1e265b54f620" + integrity sha512-YMuIJl19vQT1+g/OU9mLY6T5ZBT9uDlmeXExDQACpGuxTJW+LHNbk/lRX+eCApQI2eLBlaL4U68r3kZlqwbdmw== + dependencies: + "@types/node" "12.20.24" + colors "~1.2.1" + fs-extra "~7.0.1" + import-lazy "~4.0.0" + jju "~1.4.0" + resolve "~1.17.0" + semver "~7.3.0" + timsort "~0.3.0" + z-schema "~5.0.2" + +"@rushstack/rig-package@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.3.7.tgz#3fa564b1d129d28689dd4309502792b15e84bf81" + integrity sha512-pzMsTSeTC8IiZ6EJLr53gGMvhT4oLWH+hxD7907cHyWuIUlEXFtu/2pK25vUQT13nKp5DJCWxXyYoGRk/h6rtA== + dependencies: + resolve "~1.17.0" + strip-json-comments "~3.1.1" + +"@rushstack/ts-command-line@4.10.6": + version "4.10.6" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.10.6.tgz#5669e481e4339ceb4e1428183eb0937d3bc3841b" + integrity sha512-Y3GkUag39sTIlukDg9mUp8MCHrrlJ27POrBNRQGc/uF+VVgX8M7zMzHch5zP6O1QVquWgD7Engdpn2piPYaS/g== + dependencies: + "@types/argparse" "1.0.38" + argparse "~1.0.9" + colors "~1.2.1" + string-argv "~0.3.1" + "@rushstack/ts-command-line@4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.4.0.tgz#796f24681fdcbd01d463278c9e80a51ea5f73b2b" @@ -4927,6 +5002,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== +"@types/node@12.20.24": + version "12.20.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.24.tgz#c37ac69cb2948afb4cef95f424fa0037971a9a5c" + integrity sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ== + "@types/node@^14.0.10": version "14.17.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.5.tgz#b59daf6a7ffa461b5648456ca59050ba8e40ed54" @@ -5037,7 +5117,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16", "@types/react@^16.3.0", "@types/react@^16.7.17", "@types/react@^16.8.0": +"@types/react@*", "@types/react@^16", "@types/react@^16.3.0", "@types/react@^16.7.17", "@types/react@^16.8.0", "@types/react@^16.9.17": version "16.9.11" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120" integrity sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ== @@ -5045,15 +5125,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/react@^16.9.17": - version "16.14.23" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.23.tgz#37201b9f2324c5ff8fa4600dbf19079dfdffc880" - integrity sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/reactcss@*": version "1.2.6" resolved "https://registry.yarnpkg.com/@types/reactcss/-/reactcss-1.2.6.tgz#133c1e7e896f2726370d1d5a26bf06a30a038bcc" @@ -5080,11 +5151,6 @@ dependencies: "@types/node" "*" -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" @@ -6277,11 +6343,6 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - async@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -6837,11 +6898,6 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-2.4.0.tgz#838a992da9f9d737e0f4b2db0be62bb09dd0c5e8" - integrity sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg= - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -6888,11 +6944,6 @@ bmp-js@0.0.1: resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.1.tgz#5ad0147099d13a9f38aa7b99af1d6e78666ed37f" integrity sha1-WtAUcJnROp84qnuZrx1ueGZu038= -bmp-js@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a" - integrity sha1-ZBE+nHzxICs3btYHvzBibr5XsYo= - bmp-js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" @@ -6944,7 +6995,7 @@ bonjour@^3.5.0: multicast-dns "^6.0.1" multicast-dns-service-types "^1.1.0" -boolbase@^1.0.0, boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= @@ -7098,25 +7149,15 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.14.2: - version "4.14.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.2.tgz#1b3cec458a1ba87588cc5e9be62f19b6d48813ce" - integrity sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw== - dependencies: - caniuse-lite "^1.0.30001125" - electron-to-chromium "^1.3.564" - escalade "^3.0.2" - node-releases "^1.1.61" - -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== +browserslist@4.14.2, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.19.1: + version "4.19.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.3.tgz#29b7caad327ecf2859485f696f9604214bedd383" + integrity sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg== dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" + caniuse-lite "^1.0.30001312" + electron-to-chromium "^1.4.71" escalade "^3.1.1" - node-releases "^2.0.1" + node-releases "^2.0.2" picocolors "^1.0.0" bs-logger@0.x: @@ -7457,16 +7498,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001297: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001301.tgz#ebc9086026534cab0dab99425d9c3b4425e5f450" integrity sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA== -caniuse-lite@^1.0.30001125: +caniuse-lite@^1.0.30001312: version "1.0.30001312" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz#e11eba4b87e24d22697dae05455d5aea28550d5f" integrity sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ== -caniuse-lite@^1.0.30001286: - version "1.0.30001311" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz#682ef3f4e617f1a177ad943de59775ed3032e511" - integrity sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A== - capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -7754,20 +7790,13 @@ classnames@^2.2.5, classnames@^2.2.6: resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -clean-css@4.2.x, clean-css@^4.2.3: +clean-css@4.2.x, clean-css@^4.2.3, clean-css@^5.1.5, clean-css@^5.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== dependencies: source-map "~0.6.0" -clean-css@^5.1.5, clean-css@^5.2.2: - version "5.2.4" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.4.tgz#982b058f8581adb2ae062520808fb2429bd487a4" - integrity sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg== - dependencies: - source-map "~0.6.0" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -8776,17 +8805,7 @@ css-select-base-adapter@^0.1.1: resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== -css-select@^2.0.0, css-select@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-select@^4.1.3: +css-select@^2.0.0, css-select@^2.0.2, css-select@^4.1.3, css-select@^4.2.1, css-select@~1.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.2.1.tgz#9e665d6ae4c7f9d65dbe69d0316e3221fb274cdd" integrity sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ== @@ -8797,16 +8816,6 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" @@ -8835,16 +8844,6 @@ css-vendor@^0.3.8: dependencies: is-in-browser "^1.0.2" -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - css-what@^5.0.1, css-what@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" @@ -9013,11 +9012,6 @@ csstype@^2.2.0, csstype@^2.3.0, csstype@^2.5.7, csstype@^2.5.8: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.16.tgz#544d69f547013b85a40d15bff75db38f34fe9c39" integrity sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q== -csstype@^3.0.2: - version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -9503,7 +9497,7 @@ dns-equal@^1.0.0: resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= -dns-packet@^1.3.1: +dns-packet@^1.3.1, dns-packet@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== @@ -9645,15 +9639,7 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.0: dependencies: domelementtype "^2.2.0" -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1, domutils@^1.7.0: +domutils@^1.5.1: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== @@ -9782,16 +9768,11 @@ ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== -electron-to-chromium@^1.3.564: +electron-to-chromium@^1.4.71: version "1.4.71" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.71.tgz#17056914465da0890ce00351a3b946fd4cd51ff6" integrity sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw== -electron-to-chromium@^1.4.17: - version "1.4.68" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz#d79447b6bd1bec9183f166bb33d4bef0d5e4e568" - integrity sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA== - element-resize-detector@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.3.tgz#5078d9b99398fe4c589f8c8df94ff99e5d413ff3" @@ -10134,11 +10115,6 @@ es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-promise@^3.0.2: - version "3.3.1" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" - integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= - es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -10164,7 +10140,7 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" -escalade@^3.0.2, escalade@^3.1.1: +escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== @@ -10646,7 +10622,7 @@ exenv-es6@^1.0.0: resolved "https://registry.yarnpkg.com/exenv-es6/-/exenv-es6-1.0.0.tgz#bd459136369af17cf33f959b5af58803d4068c80" integrity sha512-fcG/TX8Ruv9Ma6PBaiNsUrHRJzVzuFMP6LtPn/9iqR+nr9mcLeEOGzXQGLC5CVQSXGE98HtzW2mTZkrCA3XrDg== -exif-parser@^0.1.12, exif-parser@^0.1.9: +exif-parser@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.12.tgz#58a9d2d72c02c1f6f02a0ef4a9166272b7760922" integrity sha1-WKnS1ywCwfbwKg70qRZicrd2CSI= @@ -10892,6 +10868,13 @@ fast-url-parser@1.1.3: dependencies: punycode "^1.3.2" +fast-xml-parser@^3.19.0: + version "3.21.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736" + integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg== + dependencies: + strnum "^1.0.4" + fastest-levenshtein@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" @@ -11041,7 +11024,7 @@ file-system-cache@^1.0.5: fs-extra "^0.30.0" ramda "^0.21.0" -file-type@^3.1.0, file-type@^3.8.0: +file-type@^3.8.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= @@ -11714,35 +11697,13 @@ glob-base@^0.3.0: glob-parent "^2.0.0" is-glob "^2.0.0" -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.0.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: +glob-parent@^2.0.0, glob-parent@^3.1.0, glob-parent@^5.0.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@^6.0.1, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - glob-promise@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" @@ -12363,11 +12324,6 @@ hsla-regex@^1.0.0: resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - html-element-map@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.3.0.tgz#fcf226985d7111e6c2b958169312ec750d02f0d3" @@ -12822,12 +12778,7 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= -immer@8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656" - integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA== - -immer@^9.0.7: +immer@8.0.1, immer@^9.0.6, immer@^9.0.7: version "9.0.12" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.12.tgz#2d33ddf3ee1d247deab9d707ca472c8c942a0f20" integrity sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA== @@ -12853,6 +12804,11 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +import-lazy@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -13040,11 +12996,6 @@ invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -ip-regex@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" - integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -13316,7 +13267,7 @@ is-glob@^2.0.0: dependencies: is-extglob "^1.0.0" -is-glob@^3.0.0, is-glob@^3.1.0: +is-glob@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= @@ -13525,12 +13476,12 @@ is-subset@^0.1.1: resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== +is-svg@^3.0.0, is-svg@^4.2.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-4.3.2.tgz#a119e9932e1af53f6be1969d1790d6cc5fd947d3" + integrity sha512-mM90duy00JGMyjqIVHu9gNTjywdZV+8qNasX8cm/EEYZ53PHDgajvbBwNVvty5dwSAxLUD3p3bdo+7sR/UMrpw== dependencies: - html-comment-regex "^1.1.0" + fast-xml-parser "^3.19.0" is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" @@ -14263,7 +14214,7 @@ jest@^25.4.0: import-local "^3.0.2" jest-cli "^25.5.4" -jimp@^0.16.1: +jimp@^0.16.1, jimp@^0.2.21: version "0.16.1" resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.16.1.tgz#192f851a30e5ca11112a3d0aa53137659a78ca7a" integrity sha512-+EKVxbR36Td7Hfd23wKGIeEyHbxShZDX6L8uJkgVW3ESA9GiTEPK08tG1XI2r/0w5Ch0HyJF5kPqF9K7EmGjaw== @@ -14274,28 +14225,6 @@ jimp@^0.16.1: "@jimp/types" "^0.16.1" regenerator-runtime "^0.13.3" -jimp@^0.2.21: - version "0.2.28" - resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.2.28.tgz#dd529a937190f42957a7937d1acc3a7762996ea2" - integrity sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI= - dependencies: - bignumber.js "^2.1.0" - bmp-js "0.0.3" - es6-promise "^3.0.2" - exif-parser "^0.1.9" - file-type "^3.1.0" - jpeg-js "^0.2.0" - load-bmfont "^1.2.3" - mime "^1.3.4" - mkdirp "0.5.1" - pixelmatch "^4.0.0" - pngjs "^3.0.0" - read-chunk "^1.0.1" - request "^2.65.0" - stream-to-buffer "^0.1.0" - tinycolor2 "^1.1.2" - url-regex "^3.0.0" - jju@^1.4.0, jju@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" @@ -14321,22 +14250,7 @@ joi@^17.4.0, joi@^17.4.2: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -jpeg-js@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.2.tgz#8b345b1ae4abde64c2da2fe67ea216a114ac279d" - integrity sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw== - -jpeg-js@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.1.2.tgz#135b992c0575c985cfa0f494a3227ed238583ece" - integrity sha1-E1uZLAV1yYXPoPSUoyJ+0jhYPs4= - -jpeg-js@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" - integrity sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII= - -jpeg-js@^0.4.2: +jpeg-js@0.4.2, jpeg-js@^0.1.1, jpeg-js@^0.4.2, jpeg-js@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b" integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q== @@ -14356,23 +14270,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@3.13.1, js-yaml@~3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@3.x, js-yaml@^3.13.1: +js-yaml@3.13.1, js-yaml@3.14.0, js-yaml@3.x, js-yaml@^3.13.1, js-yaml@^4.0.0, js-yaml@^4.1.0, js-yaml@~3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -14380,13 +14278,6 @@ js-yaml@3.x, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -15108,7 +14999,7 @@ lit@^2.0.0: lit-element "^3.1.0" lit-html "^2.1.0" -load-bmfont@^1.2.3, load-bmfont@^1.3.1, load-bmfont@^1.4.0: +load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.1.tgz#c0f5f4711a1e2ccff725a7b6078087ccfcddd3e9" integrity sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA== @@ -16027,11 +15918,6 @@ minimist-options@^3.0.1: arrify "^1.0.1" is-plain-obj "^1.1.0" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -16155,13 +16041,6 @@ mkdirp@*, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= - dependencies: - minimist "0.0.8" - mkdirp@0.5.5, mkdirp@0.5.x, mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -16342,15 +16221,10 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== -nanoid@3.1.12: - version "3.1.12" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" - integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== - -nanoid@^3.1.30: - version "3.2.0" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" - integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@3.1.12, nanoid@^3.1.30, nanoid@^3.1.31: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== nanomatch@^1.2.9: version "1.2.13" @@ -16569,23 +16443,19 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" - integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== +node-notifier@^6.0.0, node-notifier@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-9.0.1.tgz#cea837f4c5e733936c7b9005e6545cea825d1af4" + integrity sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg== dependencies: growly "^1.3.0" - is-wsl "^2.1.1" - semver "^6.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" shellwords "^0.1.1" - which "^1.3.1" - -node-releases@^1.1.61: - version "1.1.77" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" - integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== + uuid "^8.3.0" + which "^2.0.2" -node-releases@^2.0.1: +node-releases@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== @@ -16813,13 +16683,6 @@ nprogress@^0.2.0: resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= -nth-check@^1.0.2, nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - nth-check@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" @@ -17529,11 +17392,6 @@ path-browserify@0.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -17694,7 +17552,7 @@ pirates@^4.0.0, pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" -pixelmatch@^4.0.0, pixelmatch@^4.0.2: +pixelmatch@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" integrity sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ= @@ -19370,11 +19228,6 @@ reactcss@^1.2.0: dependencies: lodash "^4.0.1" -read-chunk@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" - integrity sha1-X2jKswfmY/GZk1J9m1icrORmEZQ= - read-cmd-shim@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz#87e43eba50098ba5a32d0ceb583ab8e43b961c16" @@ -19890,7 +19743,7 @@ request-promise-native@^1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.65.0, request@^2.88.0, request@^2.88.2: +request@^2.88.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -20524,7 +20377,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@~7.3.0: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -21301,18 +21154,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-buffer@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9" - integrity sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk= - dependencies: - stream-to "~0.2.0" - -stream-to@~0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d" - integrity sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0= - streamroller@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-1.0.6.tgz#8167d8496ed9f19f05ee4b158d9611321b8cacd9" @@ -21343,7 +21184,7 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= -string-argv@0.3.1: +string-argv@0.3.1, string-argv@~0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== @@ -21581,11 +21422,16 @@ strip-json-comments@2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@3.1.1, strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strnum@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + strong-log-transformer@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -22057,7 +21903,7 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3: resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== -tinycolor2@^1.1.2, tinycolor2@^1.4.1, tinycolor2@^1.4.2: +tinycolor2@^1.4.1, tinycolor2@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803" integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA== @@ -22243,20 +22089,10 @@ tree-kill@^1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - -trim-newlines@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" - integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= - -trim-newlines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" - integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== +trim-newlines@^1.0.0, trim-newlines@^2.0.0, trim-newlines@^3.0.0, trim-newlines@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.0.2.tgz#d6aaaf6a0df1b4b536d183879a6b939489808c7c" + integrity sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew== trim-off-newlines@^1.0.0: version "1.0.3" @@ -22273,10 +22109,10 @@ trim-trailing-lines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= +trim@0.0.1, trim@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.3.tgz#05243a47a3a4113e6b49367880a9cca59697a20b" + integrity sha512-h82ywcYhHK7veeelXrCScdH7HkWfbIT1D/CgYO+nmDarz3SGNssVBMws6jU16Ga60AJCRAvPV6w6RLuNerQqjg== trough@^1.0.0: version "1.0.5" @@ -22518,22 +22354,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.8.3, typescript@^3.9.0, "typescript@~3.9.0 || ~4.3.5": +typescript@^3.8.3, typescript@^3.9.0, typescript@~3.7.2, "typescript@~3.9.0 || ~4.3.5", typescript@~4.5.2: version "3.9.7" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== -typescript@~3.7.2: - version "3.7.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.7.tgz#c931733e2ec10dda56b855b379cc488a72a81199" - integrity sha512-MmQdgo/XenfZPvVLtKZOq9jQQvzaUAUpcKW8Z43x9B2fOm4S5g//tPtMweZUIP+SoBqrVPEIm+dJeQ9dfO0QdA== - -ua-parser-js@0.7.22: - version "0.7.22" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3" - integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q== - -ua-parser-js@^0.7.18: +ua-parser-js@0.7.22, ua-parser-js@^0.7.18, ua-parser-js@^0.7.28: version "0.7.31" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== @@ -22851,21 +22677,14 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.4.7: - version "1.5.4" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.4.tgz#e4f645a7e2a0852cc8a66b14b292a3e9a11a97fd" - integrity sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg== +url-parse@^1.4.3, url-parse@^1.4.7, url-parse@^1.5.0: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" -url-regex@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/url-regex/-/url-regex-3.2.0.tgz#dbad1e0c9e29e105dd0b1f09f6862f7fdb482724" - integrity sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ= - dependencies: - ip-regex "^1.0.1" - url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -22969,7 +22788,7 @@ uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.1: +uuid@^8.3.0, uuid@^8.3.1: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -23003,6 +22822,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validator@^13.7.0: + version "13.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" + integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== + validator@^8.0.0: version "8.2.0" resolved "https://registry.yarnpkg.com/validator/-/validator-8.2.0.tgz#3c1237290e37092355344fef78c231249dab77b9" @@ -23950,23 +23774,11 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^6.0.0, ws@^6.2.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -ws@^7.0.0, ws@^7.3.1, ws@^7.4.5, ws@^7.4.6, ws@~7.4.2: +ws@^6.0.0, ws@^6.2.1, ws@^7.0.0, ws@^7.3.1, ws@^7.4.5, ws@^7.4.6, ws@^8.1.0, ws@~7.4.2: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@^8.1.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -24017,10 +23829,10 @@ xmlchars@^2.1.1, xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= +xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4: + version "1.6.3" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6" + integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.2" @@ -24209,6 +24021,17 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" +z-schema@~5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.2.tgz#f410394b2c9fcb9edaf6a7511491c0bb4e89a504" + integrity sha512-40TH47ukMHq5HrzkeVE40Ad7eIDKaRV2b+Qpi2prLc9X9eFJFzV7tMe5aH12e6avaSS/u5l653EQOv+J9PirPw== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^2.7.1" + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" From 588f1c771ec4a688091b90d0ebcdf9e6f9fb2bad Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Wed, 23 Feb 2022 13:53:16 -0800 Subject: [PATCH 17/29] move template-parser files to own directory --- .../src/{element-renderer => template-parser}/marker.ts | 0 .../{element-renderer => template-parser}/op-codes.ts | 0 .../template-parser.spec.ts | 0 .../template-parser.ts | 9 ++++----- 4 files changed, 4 insertions(+), 5 deletions(-) rename packages/web-components/fast-ssr/src/{element-renderer => template-parser}/marker.ts (100%) rename packages/web-components/fast-ssr/src/{element-renderer => template-parser}/op-codes.ts (100%) rename packages/web-components/fast-ssr/src/{element-renderer => template-parser}/template-parser.spec.ts (100%) rename packages/web-components/fast-ssr/src/{element-renderer => template-parser}/template-parser.ts (99%) diff --git a/packages/web-components/fast-ssr/src/element-renderer/marker.ts b/packages/web-components/fast-ssr/src/template-parser/marker.ts similarity index 100% rename from packages/web-components/fast-ssr/src/element-renderer/marker.ts rename to packages/web-components/fast-ssr/src/template-parser/marker.ts diff --git a/packages/web-components/fast-ssr/src/element-renderer/op-codes.ts b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts similarity index 100% rename from packages/web-components/fast-ssr/src/element-renderer/op-codes.ts rename to packages/web-components/fast-ssr/src/template-parser/op-codes.ts diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts similarity index 100% rename from packages/web-components/fast-ssr/src/element-renderer/template-parser.spec.ts rename to packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts diff --git a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts similarity index 99% rename from packages/web-components/fast-ssr/src/element-renderer/template-parser.ts rename to packages/web-components/fast-ssr/src/template-parser/template-parser.ts index c2db2268627..e018851c722 100644 --- a/packages/web-components/fast-ssr/src/element-renderer/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -3,10 +3,10 @@ * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. */ import { - ViewTemplate, AspectedHTMLDirective, - HTMLDirective, DOM, + HTMLDirective, + ViewTemplate, } from "@microsoft/fast-element"; import { DefaultTreeCommentNode, @@ -16,14 +16,13 @@ import { DefaultTreeParentNode, DefaultTreeTextNode, parseFragment, - CommentNode, } from "parse5"; -import { Op, OpType } from "./op-codes.js"; import { + extractInterpolationMarkerId, isInterpolationMarker, isMarkerComment, - extractInterpolationMarkerId, } from "./marker.js"; +import { Op, OpType } from "./op-codes.js"; const opCache: Map = new Map(); From 661eb05cb5a77dc03898a0c102c74e352c7e4788 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Wed, 23 Feb 2022 14:10:11 -0800 Subject: [PATCH 18/29] adding tests for directive ops --- .../template-parser/template-parser.spec.ts | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index 7200c34ce77..ada9ea2c3dc 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -18,23 +18,44 @@ test.describe("parseTemplateToOpCodes", () => { }); interface Fixture { + title: string, input: ViewTemplate, result: Op[]; } const fixtures: ( (()=> Fixture) | Fixture )[] = [ - {input: html`

Hello world

`, result: [{type: OpType.text, value: "

Hello world

"}]}, - {input: html``, result: [{type: OpType.text, value: ""}]}, + { + title: "should emit a single text op for a template with no bindings or directives", + input: html`

Hello world

`, result: [{type: OpType.text, value: "

Hello world

"}] + }, + { + title: "should emit doctype, html, head, and body elements as part of text op", + input: html``, result: [{type: OpType.text, value: ""}] + }, () => { const input = html`${() => "hello world"}`; - return { input, result: [{ type: OpType.directive, directive: input.directives[0]}] } as Fixture - } + return { + title: "should emit a directive op from a binding", + input, + result: [{ type: OpType.directive, directive: input.directives[0]}] } as Fixture + }, + () => { + const input = html`

${() => "hello world"}

`; + return { + title: "should sandwich directive ops between text ops when binding native element content", + input, + result: [ + { type: OpType.text, value: "

"}, + { type: OpType.directive, directive: input.directives[0]}, + { type: OpType.text, value: "

"}, + ]} as Fixture + }, ]; fixtures.forEach((fixture, index) => { - const { input, result } = typeof fixture === "function" ? fixture() : fixture; - test(`should parse template to op codes for fixture ${index}`, () => { + const { input, result, title } = typeof fixture === "function" ? fixture() : fixture; + test(title, () => { expect(parseTemplateToOpCodes(input)).toEqual(result) - }) + }); }) }) From 39d01f03a2bd2d2ac628af6ab8b55154a299ff78 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 10:03:43 -0800 Subject: [PATCH 19/29] fix-up after rebase --- packages/web-components/fast-ssr/src/template-parser/marker.ts | 3 ++- .../fast-ssr/src/template-parser/template-parser.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/marker.ts b/packages/web-components/fast-ssr/src/template-parser/marker.ts index 4aa22f1061a..fc78b67e19d 100644 --- a/packages/web-components/fast-ssr/src/template-parser/marker.ts +++ b/packages/web-components/fast-ssr/src/template-parser/marker.ts @@ -1,6 +1,7 @@ -import { marker } from "@microsoft/fast-element"; import { DefaultTreeCommentNode } from "parse5"; +import { Markup } from "@microsoft/fast-element"; +const { marker } = Markup; const blockMarker = new RegExp(`${marker}:\\d+`); export function isMarkerComment(node: DefaultTreeCommentNode): boolean { return blockMarker.test(node.data); diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index e018851c722..4d301bc4aa5 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -7,6 +7,7 @@ import { DOM, HTMLDirective, ViewTemplate, + Markup, } from "@microsoft/fast-element"; import { DefaultTreeCommentNode, @@ -112,7 +113,7 @@ class TemplateParser implements Visitor { const directive = this.directives[ this.isInterpolationMarkerNode(node) ? extractInterpolationMarkerId(node)! - : DOM.extractDirectiveIndexFromMarker((node as unknown) as Comment) + : Markup.indexFromComment((node as unknown) as Comment) ]; if (directive instanceof AspectedHTMLDirective) { this.opCodes.push({ type: OpType.directive, directive }); From b37032a7a4db79b59d867fccb00c95deec3ae3f5 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 11:21:57 -0800 Subject: [PATCH 20/29] emit op-codes for custom elements --- .../fast-ssr/src/template-parser/op-codes.ts | 46 ++++++++++++- .../template-parser/template-parser.spec.ts | 69 +++++++++---------- .../src/template-parser/template-parser.ts | 55 ++++++++++++++- 3 files changed, 132 insertions(+), 38 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts index dfc3175aabe..152ef101b12 100644 --- a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts +++ b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts @@ -4,9 +4,12 @@ import { AspectedHTMLDirective } from "@microsoft/fast-element"; * Allows fast identification of operation types */ export enum OpType { - text, + customElementOpen, customElementClose, + customElementAttributes, + customElementShadow, directive, + text, } /** @@ -17,6 +20,28 @@ export type TextOp = { value: string; }; +/** + * Operation to open a custom element + */ +export type CustomElementOpenOp = { + type: OpType.customElementOpen; + /** + * The tagname of the custom element + */ + tagName: string; + + /** + * The constructor of the custom element + */ + ctor: typeof HTMLElement; + + /** + * Attributes of the custom element, non-inclusive of any attributes + * that are the product of bindings + */ + staticAttributes: Map; +}; + /** * Operation to close a custom element */ @@ -24,6 +49,10 @@ export type CustomElementCloseOp = { type: OpType.customElementClose; }; +export type CustomElementShadowOp = { + type: OpType.customElementShadow; +}; + /** * Operation to emit static text */ @@ -32,4 +61,17 @@ export type DirectiveOp = { directive: AspectedHTMLDirective; }; -export type Op = TextOp | CustomElementCloseOp | DirectiveOp; +/** + * Operation to emit to custom-element attributes + */ +export type CustomElementAttributes = { + type: OpType.customElementAttributes; +}; + +export type Op = + | TextOp + | CustomElementOpenOp + | CustomElementCloseOp + | DirectiveOp + | CustomElementAttributes + | CustomElementShadowOp; diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index ada9ea2c3dc..58c41f6e6c4 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -2,9 +2,12 @@ import "@lit-labs/ssr/lib/install-global-dom-shim.js"; import { test, expect } from "@playwright/test"; import { parseTemplateToOpCodes} from "./template-parser.js"; -import { ViewTemplate, html } from "@microsoft/fast-element" +import { ViewTemplate, html, FASTElement, customElement } from "@microsoft/fast-element" import { Op, OpType } from "./op-codes.js"; +@customElement("hello-world") +class HelloWorld extends FASTElement {} + test.describe("parseTemplateToOpCodes", () => { test("should throw when invoked with a ViewTemplate with a HTMLTemplateElement template", () => { expect(() => { @@ -17,45 +20,41 @@ test.describe("parseTemplateToOpCodes", () => { }).not.toThrow(); }); - interface Fixture { - title: string, - input: ViewTemplate, - result: Op[]; - } - - const fixtures: ( (()=> Fixture) | Fixture )[] = [ - { - title: "should emit a single text op for a template with no bindings or directives", - input: html`

Hello world

`, result: [{type: OpType.text, value: "

Hello world

"}] - }, - { - title: "should emit doctype, html, head, and body elements as part of text op", - input: html``, result: [{type: OpType.text, value: ""}] - }, - () => { + test("should emit a single text op for a template with no bindings or directives", () => { + expect(parseTemplateToOpCodes(html`

Hello world

`)).toEqual([{type: OpType.text, value: "

Hello world

"}]) + }); + test("should emit doctype, html, head, and body elements as part of text op", () => { + expect(parseTemplateToOpCodes(html``)).toEqual([{type: OpType.text, value: ""}]) + }) + test("should emit a directive op from a binding", () => { const input = html`${() => "hello world"}`; - return { - title: "should emit a directive op from a binding", - input, - result: [{ type: OpType.directive, directive: input.directives[0]}] } as Fixture - }, - () => { + expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.directive, directive: input.directives[0]}]) + }) + + test("should sandwich directive ops between text ops when binding native element content", () => { + const input = html`

${() => "hello world"}

`; - return { - title: "should sandwich directive ops between text ops when binding native element content", - input, - result: [ + expect(parseTemplateToOpCodes(input)).toEqual([ { type: OpType.text, value: "

"}, { type: OpType.directive, directive: input.directives[0]}, { type: OpType.text, value: "

"}, - ]} as Fixture - }, - ]; - - fixtures.forEach((fixture, index) => { - const { input, result, title } = typeof fixture === "function" ? fixture() : fixture; - test(title, () => { - expect(parseTemplateToOpCodes(input)).toEqual(result) + ]) }); + test("should emit a custom element as text if it has not been defined", () => { + const input = html``; + expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.text, value: ""}]) + }) + + test("should emit custom element open, close, attribute, and shadow ops for a defined custom element", () => { + const input = html``; + expect(parseTemplateToOpCodes(input)).toEqual([ + {type: OpType.customElementOpen, ctor: HelloWorld, tagName: "hello-world", staticAttributes: new Map()}, + {type: OpType.text, value: ""}, + {type: OpType.customElementShadow}, + {type: OpType.customElementClose}, + {type: OpType.text, value: ""} + ]) }) }) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index 4d301bc4aa5..3816f38bd85 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -33,6 +33,12 @@ interface Visitor { complete?: () => void; } +declare module "parse5" { + interface DefaultTreeElement { + isDefinedCustomElement?: boolean; + } +} + // Will be 0 when starting and ending traversal. let counter = 0; /** @@ -122,15 +128,62 @@ class TemplateParser implements Visitor { `Unexpected directive type encountered. It is a ${directive}.` ); } + this.skipTo(node.sourceCodeLocation.endOffset); + } else if (isElementNode(node)) { + this.collectElementOps(node); + } + } + + public leave(node: DefaultTreeNode): void { + if (isElementNode(node) && node.isDefinedCustomElement) { + this.opCodes.push({ type: OpType.customElementClose }); } } - public leave(node: DefaultTreeNode): void {} public complete() { this.flushTo(); } + private collectElementOps(node: DefaultTreeElement): void { + let writeTag = false; + const { tagName } = node; + let ctor: typeof HTMLElement | undefined; + + // If custom element + if (node.tagName.includes("-")) { + ctor = customElements.get(tagName); + + if (ctor !== undefined) { + writeTag = true; + node.isDefinedCustomElement = true; + this.opCodes.push({ + type: OpType.customElementOpen, + tagName, + ctor, + staticAttributes: new Map( + node.attrs.map(attr => [attr.name, attr.value]) + ), + }); + } + } + + if (writeTag) { + if (ctor) { + this.flushTo(node.sourceCodeLocation!.startTag.endOffset - 1); + this.opCodes.push({ type: OpType.customElementAttributes }); + this.flush(">"); + this.skipTo(node.sourceCodeLocation!.startTag.endOffset); + } else { + this.flushTo(node.sourceCodeLocation!.startTag.endOffset); + } + } + + if (ctor !== undefined) { + this.opCodes.push({ type: OpType.customElementShadow }); + } + } + /** * Flushes a string value to op codes * @param value - The value to flush From f137af3538494a98d087ebd6f96fd8e06922ac20 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 13:10:54 -0800 Subject: [PATCH 21/29] adding attribute binding ops --- .../src/template-parser/attributes.ts | 14 +++++ .../fast-ssr/src/template-parser/op-codes.ts | 14 +++++ .../template-parser/template-parser.spec.ts | 23 +++++++- .../src/template-parser/template-parser.ts | 56 ++++++++++++++++++- 4 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 packages/web-components/fast-ssr/src/template-parser/attributes.ts diff --git a/packages/web-components/fast-ssr/src/template-parser/attributes.ts b/packages/web-components/fast-ssr/src/template-parser/attributes.ts new file mode 100644 index 00000000000..8e4f322a44a --- /dev/null +++ b/packages/web-components/fast-ssr/src/template-parser/attributes.ts @@ -0,0 +1,14 @@ +/** + * Extracts the attribute type from an attribute name + */ +export const attributeTypeRegExp = /([:?@])?(.*)/; + +/** + * The types of attributes applied in a template + */ +export enum AttributeType { + content, + booleanContent, + idl, + event, +} diff --git a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts index 152ef101b12..857dd2ff0b9 100644 --- a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts +++ b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts @@ -1,4 +1,5 @@ import { AspectedHTMLDirective } from "@microsoft/fast-element"; +import { AttributeType } from "./attributes"; /** * Allows fast identification of operation types @@ -8,6 +9,7 @@ export enum OpType { customElementClose, customElementAttributes, customElementShadow, + attributeBinding, directive, text, } @@ -61,6 +63,17 @@ export type DirectiveOp = { directive: AspectedHTMLDirective; }; +/** + * Operation to emit a bound attribute + */ +export type AttributeBindingOp = { + type: OpType.attributeBinding; + directive: AspectedHTMLDirective; + name: string; + attributeType: AttributeType; + useCustomElementInstance: boolean; +}; + /** * Operation to emit to custom-element attributes */ @@ -69,6 +82,7 @@ export type CustomElementAttributes = { }; export type Op = + | AttributeBindingOp | TextOp | CustomElementOpenOp | CustomElementCloseOp diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index 58c41f6e6c4..815ba3a5f99 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -3,7 +3,8 @@ import "@lit-labs/ssr/lib/install-global-dom-shim.js"; import { test, expect } from "@playwright/test"; import { parseTemplateToOpCodes} from "./template-parser.js"; import { ViewTemplate, html, FASTElement, customElement } from "@microsoft/fast-element" -import { Op, OpType } from "./op-codes.js"; +import { Op, OpType, CustomElementOpenOp, AttributeBindingOp } from "./op-codes.js"; +import { AttributeType } from "./attributes.js"; @customElement("hello-world") class HelloWorld extends FASTElement {} @@ -56,5 +57,23 @@ test.describe("parseTemplateToOpCodes", () => { {type: OpType.customElementClose}, {type: OpType.text, value: ""} ]) - }) + }); + test("should emit static attributes of a custom element custom element open, close, attribute, and shadow ops for a defined custom element", () => { + const input = html``; + const code = parseTemplateToOpCodes(input).find((op) => op.type ===OpType.customElementOpen) as CustomElementOpenOp | undefined ; + expect(code).not.toBeUndefined(); + expect(code?.staticAttributes.get("string-value")).toBe("test"); + expect(code?.staticAttributes.get("bool-value")).toBe(""); + expect(code?.staticAttributes.size).toBe(2); + }); + test("should emit attributes binding ops for a native element with attribute bindings", () => { + const input = html`

`; + const codes = parseTemplateToOpCodes(input).filter(x => x.type === OpType.attributeBinding) as AttributeBindingOp[]; + + expect(codes.length).toBe(4); + expect(codes[0].attributeType).toBe(AttributeType.content); + expect(codes[1].attributeType).toBe(AttributeType.booleanContent); + expect(codes[2].attributeType).toBe(AttributeType.idl); + expect(codes[3].attributeType).toBe(AttributeType.event); + }); }) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index 3816f38bd85..384de45ffcf 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -8,6 +8,8 @@ import { HTMLDirective, ViewTemplate, Markup, + attr, + Parser, } from "@microsoft/fast-element"; import { DefaultTreeCommentNode, @@ -17,6 +19,7 @@ import { DefaultTreeParentNode, DefaultTreeTextNode, parseFragment, + Attribute, } from "parse5"; import { extractInterpolationMarkerId, @@ -24,6 +27,7 @@ import { isMarkerComment, } from "./marker.js"; import { Op, OpType } from "./op-codes.js"; +import { AttributeType, attributeTypeRegExp } from "./attributes.js"; const opCache: Map = new Map(); @@ -149,6 +153,21 @@ class TemplateParser implements Visitor { let writeTag = false; const { tagName } = node; let ctor: typeof HTMLElement | undefined; + const attributes: { + static: Map; + dynamic: Attribute[]; + } = node.attrs.reduce( + (prev, current) => { + if (isInterpolationMarker(current)) { + prev.dynamic.push(current); + } else { + prev.static.set(current.name, current.value); + } + + return prev; + }, + { static: new Map(), dynamic: [] as Attribute[] } + ); // If custom element if (node.tagName.includes("-")) { @@ -161,13 +180,32 @@ class TemplateParser implements Visitor { type: OpType.customElementOpen, tagName, ctor, - staticAttributes: new Map( - node.attrs.map(attr => [attr.name, attr.value]) - ), + staticAttributes: attributes.static, }); } } + if (attributes.dynamic.length) { + for (let attr of attributes.dynamic) { + const location = node.sourceCodeLocation!.attrs[attr.name]; + this.flushTo(location.startOffset); + const attributeType = this.getAttributeType(attr); + const parsed = Parser.parse(attr.value, this.directives); + + if (parsed !== null) { + writeTag = true; + this.opCodes.push({ + type: OpType.attributeBinding, + name: attr.name, + directive: Parser.aggregate(parsed), + attributeType, + useCustomElementInstance: Boolean(node.isDefinedCustomElement), + }); + this.skipTo(location.endOffset); + } + } + } + if (writeTag) { if (ctor) { this.flushTo(node.sourceCodeLocation!.startTag.endOffset - 1); @@ -184,6 +222,18 @@ class TemplateParser implements Visitor { } } + private getAttributeType(attr: Attribute): AttributeType { + const [, prefix] = attributeTypeRegExp.exec(attr.name)!; + + return prefix === ":" + ? AttributeType.idl + : prefix === "?" + ? AttributeType.booleanContent + : prefix === "@" + ? AttributeType.event + : AttributeType.content; + } + /** * Flushes a string value to op codes * @param value - The value to flush From 5a3b6b543905f2a56e3f190138d03e8efe36530c Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 14:35:36 -0800 Subject: [PATCH 22/29] formatting --- .../template-parser/template-parser.spec.ts | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index 815ba3a5f99..2f1b7e088b2 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -10,70 +10,70 @@ import { AttributeType } from "./attributes.js"; class HelloWorld extends FASTElement {} test.describe("parseTemplateToOpCodes", () => { - test("should throw when invoked with a ViewTemplate with a HTMLTemplateElement template", () => { - expect(() => { - parseTemplateToOpCodes(new ViewTemplate(document.createElement("template"), [])); - }).toThrow(); - }); - test("should not throw when invoked with a ViewTemplate with a string template", () => { - expect(() => { - parseTemplateToOpCodes(new ViewTemplate("", [])); - }).not.toThrow(); - }); + test("should throw when invoked with a ViewTemplate with a HTMLTemplateElement template", () => { + expect(() => { + parseTemplateToOpCodes(new ViewTemplate(document.createElement("template"), [])); + }).toThrow(); + }); + test("should not throw when invoked with a ViewTemplate with a string template", () => { + expect(() => { + parseTemplateToOpCodes(new ViewTemplate("", [])); + }).not.toThrow(); + }); - test("should emit a single text op for a template with no bindings or directives", () => { - expect(parseTemplateToOpCodes(html`

Hello world

`)).toEqual([{type: OpType.text, value: "

Hello world

"}]) - }); - test("should emit doctype, html, head, and body elements as part of text op", () => { - expect(parseTemplateToOpCodes(html``)).toEqual([{type: OpType.text, value: ""}]) - }) - test("should emit a directive op from a binding", () => { - const input = html`${() => "hello world"}`; - expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.directive, directive: input.directives[0]}]) - }) + test("should emit a single text op for a template with no bindings or directives", () => { + expect(parseTemplateToOpCodes(html`

Hello world

`)).toEqual([{type: OpType.text, value: "

Hello world

"}]) + }); + test("should emit doctype, html, head, and body elements as part of text op", () => { + expect(parseTemplateToOpCodes(html``)).toEqual([{type: OpType.text, value: ""}]) + }) + test("should emit a directive op from a binding", () => { + const input = html`${() => "hello world"}`; + expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.directive, directive: input.directives[0]}]) + }) - test("should sandwich directive ops between text ops when binding native element content", () => { + test("should sandwich directive ops between text ops when binding native element content", () => { - const input = html`

${() => "hello world"}

`; - expect(parseTemplateToOpCodes(input)).toEqual([ - { type: OpType.text, value: "

"}, - { type: OpType.directive, directive: input.directives[0]}, - { type: OpType.text, value: "

"}, - ]) - }); - test("should emit a custom element as text if it has not been defined", () => { - const input = html``; - expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.text, value: ""}]) - }) + const input = html`

${() => "hello world"}

`; + expect(parseTemplateToOpCodes(input)).toEqual([ + { type: OpType.text, value: "

"}, + { type: OpType.directive, directive: input.directives[0]}, + { type: OpType.text, value: "

"}, + ]) + }); + test("should emit a custom element as text if it has not been defined", () => { + const input = html``; + expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.text, value: ""}]) + }) - test("should emit custom element open, close, attribute, and shadow ops for a defined custom element", () => { - const input = html``; - expect(parseTemplateToOpCodes(input)).toEqual([ - {type: OpType.customElementOpen, ctor: HelloWorld, tagName: "hello-world", staticAttributes: new Map()}, - {type: OpType.text, value: ""}, - {type: OpType.customElementShadow}, - {type: OpType.customElementClose}, - {type: OpType.text, value: ""} - ]) - }); - test("should emit static attributes of a custom element custom element open, close, attribute, and shadow ops for a defined custom element", () => { - const input = html``; - const code = parseTemplateToOpCodes(input).find((op) => op.type ===OpType.customElementOpen) as CustomElementOpenOp | undefined ; - expect(code).not.toBeUndefined(); - expect(code?.staticAttributes.get("string-value")).toBe("test"); - expect(code?.staticAttributes.get("bool-value")).toBe(""); - expect(code?.staticAttributes.size).toBe(2); - }); - test("should emit attributes binding ops for a native element with attribute bindings", () => { - const input = html`

`; - const codes = parseTemplateToOpCodes(input).filter(x => x.type === OpType.attributeBinding) as AttributeBindingOp[]; + test("should emit custom element open, close, attribute, and shadow ops for a defined custom element", () => { + const input = html``; + expect(parseTemplateToOpCodes(input)).toEqual([ + {type: OpType.customElementOpen, ctor: HelloWorld, tagName: "hello-world", staticAttributes: new Map()}, + {type: OpType.text, value: ""}, + {type: OpType.customElementShadow}, + {type: OpType.customElementClose}, + {type: OpType.text, value: ""} + ]) + }); + test("should emit static attributes of a custom element custom element open, close, attribute, and shadow ops for a defined custom element", () => { + const input = html``; + const code = parseTemplateToOpCodes(input).find((op) => op.type ===OpType.customElementOpen) as CustomElementOpenOp | undefined ; + expect(code).not.toBeUndefined(); + expect(code?.staticAttributes.get("string-value")).toBe("test"); + expect(code?.staticAttributes.get("bool-value")).toBe(""); + expect(code?.staticAttributes.size).toBe(2); + }); + test("should emit attributes binding ops for a native element with attribute bindings", () => { + const input = html`

`; + const codes = parseTemplateToOpCodes(input).filter(x => x.type === OpType.attributeBinding) as AttributeBindingOp[]; - expect(codes.length).toBe(4); - expect(codes[0].attributeType).toBe(AttributeType.content); - expect(codes[1].attributeType).toBe(AttributeType.booleanContent); - expect(codes[2].attributeType).toBe(AttributeType.idl); - expect(codes[3].attributeType).toBe(AttributeType.event); - }); + expect(codes.length).toBe(4); + expect(codes[0].attributeType).toBe(AttributeType.content); + expect(codes[1].attributeType).toBe(AttributeType.booleanContent); + expect(codes[2].attributeType).toBe(AttributeType.idl); + expect(codes[3].attributeType).toBe(AttributeType.event); + }); }) From 7bde7994df54c4f384e56041fa062c76838a618c Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 14:38:10 -0800 Subject: [PATCH 23/29] adding tests for custom element attribute bindings --- .../src/template-parser/template-parser.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index 2f1b7e088b2..205afeaeff4 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -70,6 +70,16 @@ test.describe("parseTemplateToOpCodes", () => { const input = html`

`; const codes = parseTemplateToOpCodes(input).filter(x => x.type === OpType.attributeBinding) as AttributeBindingOp[]; + expect(codes.length).toBe(4); + expect(codes[0].attributeType).toBe(AttributeType.content); + expect(codes[1].attributeType).toBe(AttributeType.booleanContent); + expect(codes[2].attributeType).toBe(AttributeType.idl); + expect(codes[3].attributeType).toBe(AttributeType.event); + }); + test("should emit attributes binding ops for a custom element with attribute bindings", () => { + const input = html``; + const codes = parseTemplateToOpCodes(input).filter(x => x.type === OpType.attributeBinding) as AttributeBindingOp[]; + expect(codes.length).toBe(4); expect(codes[0].attributeType).toBe(AttributeType.content); expect(codes[1].attributeType).toBe(AttributeType.booleanContent); From 41cd0a057d09c290e3a341f778a7917c2686fd66 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 14:51:14 -0800 Subject: [PATCH 24/29] organize imports --- .../src/template-parser/template-parser.ts | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index 384de45ffcf..7565f803048 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -4,30 +4,27 @@ */ import { AspectedHTMLDirective, - DOM, HTMLDirective, - ViewTemplate, Markup, - attr, Parser, + ViewTemplate, } from "@microsoft/fast-element"; import { + Attribute, DefaultTreeCommentNode, - DefaultTreeDocumentFragment, DefaultTreeElement, DefaultTreeNode, DefaultTreeParentNode, DefaultTreeTextNode, parseFragment, - Attribute, } from "parse5"; +import { AttributeType, attributeTypeRegExp } from "./attributes.js"; import { extractInterpolationMarkerId, isInterpolationMarker, isMarkerComment, } from "./marker.js"; import { Op, OpType } from "./op-codes.js"; -import { AttributeType, attributeTypeRegExp } from "./attributes.js"; const opCache: Map = new Map(); @@ -82,14 +79,6 @@ function isCommentNode(node: DefaultTreeNode): node is DefaultTreeCommentNode { return node.nodeName === "#comment"; } -/** - * Test if a node is a document fragment node. - * @param node - the node to test - */ -function isDocumentFragment(node: DefaultTreeNode): node is DefaultTreeDocumentFragment { - return node.nodeName === "#document-fragment"; -} - /** * Test if a node is a text node. * @param node - the node to test @@ -186,7 +175,7 @@ class TemplateParser implements Visitor { } if (attributes.dynamic.length) { - for (let attr of attributes.dynamic) { + for (const attr of attributes.dynamic) { const location = node.sourceCodeLocation!.attrs[attr.name]; this.flushTo(location.startOffset); const attributeType = this.getAttributeType(attr); @@ -223,7 +212,13 @@ class TemplateParser implements Visitor { } private getAttributeType(attr: Attribute): AttributeType { - const [, prefix] = attributeTypeRegExp.exec(attr.name)!; + const result = attributeTypeRegExp.exec(attr.name); + + if (result === null) { + throw new Error("Failure to determine attribute binding type"); + } + + const prefix = result[1]; return prefix === ":" ? AttributeType.idl @@ -336,7 +331,6 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { throw new Error(`Error parsing template:\n${template}`); } - const ops: Op[] = []; const visitor = new TemplateParser(html, template.directives); opCache.set(template, visitor.opCodes); From 504bbf3006750041bf8bb4ab041f6d821088da07 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 16:08:37 -0800 Subject: [PATCH 25/29] fix processing of interpolated bindings and add test --- .../fast-ssr/src/template-parser/op-codes.ts | 8 +- .../template-parser/template-parser.spec.ts | 14 +- .../src/template-parser/template-parser.ts | 329 +++++++++--------- 3 files changed, 185 insertions(+), 166 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts index 857dd2ff0b9..78caf4e25fc 100644 --- a/packages/web-components/fast-ssr/src/template-parser/op-codes.ts +++ b/packages/web-components/fast-ssr/src/template-parser/op-codes.ts @@ -1,5 +1,5 @@ -import { AspectedHTMLDirective } from "@microsoft/fast-element"; -import { AttributeType } from "./attributes"; +import { InlinableHTMLDirective } from "@microsoft/fast-element"; +import { AttributeType } from "./attributes.js"; /** * Allows fast identification of operation types @@ -60,7 +60,7 @@ export type CustomElementShadowOp = { */ export type DirectiveOp = { type: OpType.directive; - directive: AspectedHTMLDirective; + directive: InlinableHTMLDirective; }; /** @@ -68,7 +68,7 @@ export type DirectiveOp = { */ export type AttributeBindingOp = { type: OpType.attributeBinding; - directive: AspectedHTMLDirective; + directive: InlinableHTMLDirective; name: string; attributeType: AttributeType; useCustomElementInstance: boolean; diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts index 205afeaeff4..1156c0f51c4 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.spec.ts @@ -2,8 +2,8 @@ import "@lit-labs/ssr/lib/install-global-dom-shim.js"; import { test, expect } from "@playwright/test"; import { parseTemplateToOpCodes} from "./template-parser.js"; -import { ViewTemplate, html, FASTElement, customElement } from "@microsoft/fast-element" -import { Op, OpType, CustomElementOpenOp, AttributeBindingOp } from "./op-codes.js"; +import { ViewTemplate, html, FASTElement, customElement, defaultExecutionContext } from "@microsoft/fast-element" +import { Op, OpType, CustomElementOpenOp, AttributeBindingOp, DirectiveOp } from "./op-codes.js"; import { AttributeType } from "./attributes.js"; @customElement("hello-world") @@ -30,8 +30,16 @@ test.describe("parseTemplateToOpCodes", () => { test("should emit a directive op from a binding", () => { const input = html`${() => "hello world"}`; expect(parseTemplateToOpCodes(input)).toEqual([{ type: OpType.directive, directive: input.directives[0]}]) - }) + }); + test("should emit a directive op from text and a binding ", () => { + const input = html`Hello ${() => "World"}.`; + const codes = parseTemplateToOpCodes(input); + const code = codes[0] as DirectiveOp; + expect(codes.length).toBe(1); + expect(code.type).toBe(OpType.directive); + expect(code.directive.binding(null, defaultExecutionContext)).toBe("Hello World.") + }); test("should sandwich directive ops between text ops when binding native element content", () => { const input = html`

${() => "hello world"}

`; diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index 7565f803048..d7ffd159cc2 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -3,8 +3,7 @@ * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. */ import { - AspectedHTMLDirective, - HTMLDirective, + InlinableHTMLDirective, Markup, Parser, ViewTemplate, @@ -19,19 +18,17 @@ import { parseFragment, } from "parse5"; import { AttributeType, attributeTypeRegExp } from "./attributes.js"; -import { - extractInterpolationMarkerId, - isInterpolationMarker, - isMarkerComment, -} from "./marker.js"; +import { isInterpolationMarker, isMarkerComment } from "./marker.js"; import { Op, OpType } from "./op-codes.js"; +/** + * Cache the results of template parsing. + */ const opCache: Map = new Map(); interface Visitor { visit?: (node: DefaultTreeNode) => void; leave?: (node: DefaultTreeNode) => void; - complete?: () => void; } declare module "parse5" { @@ -40,15 +37,12 @@ declare module "parse5" { } } -// Will be 0 when starting and ending traversal. -let counter = 0; /** * Traverses a tree of nodes depth-first, invoking callbacks from visitor for each node as it goes. * @param node - the node to traverse * @param visitor - callbacks to be invoked during node traversal */ function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visitor) { - counter++; if (visitor.visit) { visitor.visit(node); } @@ -63,12 +57,6 @@ function traverse(node: DefaultTreeNode | DefaultTreeParentNode, visitor: Visito if (visitor.leave) { visitor.leave(node); } - - counter--; - - if (counter === 0 && visitor.complete) { - visitor.complete(); - } } /** @@ -95,53 +83,115 @@ function isElementNode(node: DefaultTreeNode): node is DefaultTreeElement { return (node as DefaultTreeElement).tagName !== undefined; } -class TemplateParser implements Visitor { - private lastOffset: number | undefined = 0; - private get lastOp() { - return this.opCodes[this.opCodes.length - 1]; +/** + * Determines which type of attribute binding an attribute is + * @param attr - The attribute to inspect + */ +function getAttributeType(attr: Attribute): AttributeType { + const result = attributeTypeRegExp.exec(attr.name); + + if (result === null) { + throw new Error("Failure to determine attribute binding type"); } - constructor(private template: string, private directives: readonly HTMLDirective[]) {} - - public readonly opCodes: Op[] = []; - public visit(node: DefaultTreeNode): void { - if (this.isInterpolationMarkerNode(node) || this.isCommentMarkerNode(node)) { - this.flushTo(node.sourceCodeLocation.startOffset); - - // TODO: clean this up when new APIs from fast-element get integrated. - const directive = this.directives[ - this.isInterpolationMarkerNode(node) - ? extractInterpolationMarkerId(node)! - : Markup.indexFromComment((node as unknown) as Comment) - ]; - if (directive instanceof AspectedHTMLDirective) { - this.opCodes.push({ type: OpType.directive, directive }); - } else { - throw new Error( - `Unexpected directive type encountered. It is a ${directive}.` - ); - } + const prefix = result[1]; - this.skipTo(node.sourceCodeLocation.endOffset); - } else if (isElementNode(node)) { - this.collectElementOps(node); - } + return prefix === ":" + ? AttributeType.idl + : prefix === "?" + ? AttributeType.booleanContent + : prefix === "@" + ? AttributeType.event + : AttributeType.content; +} + +/** + * Tests if a node is an interpolated FAST marker + * @param node - the node to test + */ +function isInterpolationMarkerNode( + node: DefaultTreeNode +): node is Required { + return ( + isTextNode(node) && + node.sourceCodeLocation !== undefined && + isInterpolationMarker(node) + ); +} + +/** + * Tests if a node is a FAST comment marker + * @param node - the node to test + */ +function isCommentMarkerNode( + node: DefaultTreeNode +): node is Required { + return ( + isCommentNode(node) && + node.sourceCodeLocation !== undefined && + isMarkerComment(node) + ); +} + +/** + * Parses a template into a set of operation instructions + * @param template - The template to parse + */ +export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { + const cached: Op[] | undefined = opCache.get(template); + if (cached !== undefined) { + return cached; } - public leave(node: DefaultTreeNode): void { - if (isElementNode(node) && node.isDefinedCustomElement) { - this.opCodes.push({ type: OpType.customElementClose }); - } + const { html } = template; + + if (typeof html !== "string") { + throw new Error( + "@microsoft/fast-ssr does not support rendering a ViewTemplate with an HTMLTemplateElement html source." + ); } - public complete() { - this.flushTo(); + /** + * Typescript thinks that `html` is a string | HTMLTemplateElement inside the functions defined + * below, so store in a new var that is just a string type + */ + const templateString = html; + const ast = parseFragment(html, { sourceCodeLocationInfo: true }); + + if (!("nodeName" in ast)) { + // I'm not sure when exactly this is encountered but the type system seems to say it's possible. + throw new Error(`Error parsing template:\n${template}`); } - private collectElementOps(node: DefaultTreeElement): void { - let writeTag = false; + /** + * Tracks the offset location in the source template string where the last + * flushing / skip took place. + */ + let lastOffset: number | undefined = 0; + + /** + * Collection of op codes + */ + const opCodes: Op[] = []; + opCache.set(template, opCodes); + + const { directives } = template; + + /** + * Parses an Element node, pushing all op codes for the element into + * the collection of ops for the template + * @param node - The element node to parse + */ + function parseElementNode(node: DefaultTreeElement): void { + // Track whether the opening tag of an element should be augmented. + // All constructable custom elements will need to be augmented, + // as well as any element with attribute bindings + let augmentOpeningTag = false; const { tagName } = node; let ctor: typeof HTMLElement | undefined; + + // Sort attributes by whether they're related to a binding or if they have + // static value const attributes: { static: Map; dynamic: Attribute[]; @@ -158,14 +208,14 @@ class TemplateParser implements Visitor { { static: new Map(), dynamic: [] as Attribute[] } ); - // If custom element + // Special processing for any custom element if (node.tagName.includes("-")) { ctor = customElements.get(tagName); if (ctor !== undefined) { - writeTag = true; + augmentOpeningTag = true; node.isDefinedCustomElement = true; - this.opCodes.push({ + opCodes.push({ type: OpType.customElementOpen, tagName, ctor, @@ -174,71 +224,55 @@ class TemplateParser implements Visitor { } } + // Push attribute binding op codes for any attributes that + // are dynamic if (attributes.dynamic.length) { for (const attr of attributes.dynamic) { const location = node.sourceCodeLocation!.attrs[attr.name]; - this.flushTo(location.startOffset); - const attributeType = this.getAttributeType(attr); - const parsed = Parser.parse(attr.value, this.directives); + flushTo(location.startOffset); + const attributeType = getAttributeType(attr); + const parsed = Parser.parse(attr.value, directives); if (parsed !== null) { - writeTag = true; - this.opCodes.push({ + augmentOpeningTag = true; + opCodes.push({ type: OpType.attributeBinding, name: attr.name, directive: Parser.aggregate(parsed), attributeType, useCustomElementInstance: Boolean(node.isDefinedCustomElement), }); - this.skipTo(location.endOffset); + skipTo(location.endOffset); } } } - if (writeTag) { + if (augmentOpeningTag) { if (ctor) { - this.flushTo(node.sourceCodeLocation!.startTag.endOffset - 1); - this.opCodes.push({ type: OpType.customElementAttributes }); - this.flush(">"); - this.skipTo(node.sourceCodeLocation!.startTag.endOffset); + flushTo(node.sourceCodeLocation!.startTag.endOffset - 1); + opCodes.push({ type: OpType.customElementAttributes }); + flush(">"); + skipTo(node.sourceCodeLocation!.startTag.endOffset); } else { - this.flushTo(node.sourceCodeLocation!.startTag.endOffset); + flushTo(node.sourceCodeLocation!.startTag.endOffset); } } if (ctor !== undefined) { - this.opCodes.push({ type: OpType.customElementShadow }); + opCodes.push({ type: OpType.customElementShadow }); } } - private getAttributeType(attr: Attribute): AttributeType { - const result = attributeTypeRegExp.exec(attr.name); - - if (result === null) { - throw new Error("Failure to determine attribute binding type"); - } - - const prefix = result[1]; - - return prefix === ":" - ? AttributeType.idl - : prefix === "?" - ? AttributeType.booleanContent - : prefix === "@" - ? AttributeType.event - : AttributeType.content; - } - /** * Flushes a string value to op codes * @param value - The value to flush */ - private flush(value: string): void { - const last = this.lastOp; + function flush(value: string): void { + const last = opCodes[opCodes.length - 1]; if (last?.type === OpType.text) { last.value += value; } else { - this.opCodes.push({ type: OpType.text, value }); + opCodes.push({ type: OpType.text, value }); } } @@ -246,95 +280,72 @@ class TemplateParser implements Visitor { * Flush template content from lastIndex to provided offset * @param offset - the offset to flush to */ - private flushTo(offset?: number) { - if (this.lastOffset === undefined) { + function flushTo(offset?: number) { + if (lastOffset === undefined) { throw new Error( - `Cannot flush template content from a last offset that is ${typeof this - .lastOffset}.` + `Cannot flush template content from a last offset that is ${typeof lastOffset}.` ); } - const prev = this.lastOffset; - this.lastOffset = offset; - const value = this.template.substring(prev, offset); + const prev = lastOffset; + lastOffset = offset; + const value = templateString.substring(prev, offset); if (value !== "") { - this.flush(value); + flush(value); } } - private skipTo(offset: number) { - if (this.lastOffset === undefined) { + function skipTo(offset: number) { + if (lastOffset === undefined) { throw new Error("Could not skip from an undefined offset"); } - if (offset < this.lastOffset) { + if (offset < lastOffset) { throw new Error(`offset must be greater than lastOffset. offset: ${offset} - lastOffset: ${this.lastOffset} + lastOffset: ${lastOffset} `); } - this.lastOffset = offset; - } - - /** - * Tests if a node is an interpolated FAST marker - * @param node - the node to test - */ - private isInterpolationMarkerNode( - node: DefaultTreeNode - ): node is Required { - return ( - isTextNode(node) && - node.sourceCodeLocation !== undefined && - isInterpolationMarker(node) - ); - } - - /** - * Tests if a node is a FAST comment marker - * @param node - the node to test - */ - private isCommentMarkerNode( - node: DefaultTreeNode - ): node is Required { - return ( - isCommentNode(node) && - node.sourceCodeLocation !== undefined && - isMarkerComment(node) - ); - } -} - -/** - * Parses a template into a set of operation instructions - * @param template - The template to parse - */ -export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { - const cached: Op[] | undefined = opCache.get(template); - if (cached !== undefined) { - return cached; - } - - const { html } = template; - - if (typeof html !== "string") { - throw new Error( - "@microsoft/fast-ssr does not support rendering a ViewTemplate with an HTMLTemplateElement html source." - ); + lastOffset = offset; } - const ast = parseFragment(html, { sourceCodeLocationInfo: true }); + traverse(ast, { + visit(node: DefaultTreeNode): void { + if (isCommentMarkerNode(node)) { + flushTo(node.sourceCodeLocation.startOffset); + const directive = directives[ + Markup.indexFromComment((node as unknown) as Comment) + ] as InlinableHTMLDirective; + + opCodes.push({ type: OpType.directive, directive }); + skipTo(node.sourceCodeLocation.endOffset); + } else if (isInterpolationMarkerNode(node)) { + flushTo(node.sourceCodeLocation.startOffset); + const parsed = Parser.parse(node.value, directives); + + if (parsed) { + opCodes.push({ + type: OpType.directive, + directive: Parser.aggregate(parsed), + }); + } - if (!("nodeName" in ast)) { - // I'm not sure when exactly this is encountered but the type system seems to say it's possible. - throw new Error(`Error parsing template:\n${template}`); - } + skipTo(node.sourceCodeLocation.endOffset); + } else if (isElementNode(node)) { + parseElementNode(node); + } + }, - const visitor = new TemplateParser(html, template.directives); - opCache.set(template, visitor.opCodes); + leave(node: DefaultTreeNode): void { + if (isElementNode(node) && node.isDefinedCustomElement) { + opCodes.push({ type: OpType.customElementClose }); + } + }, + }); - traverse(ast, visitor); + // Flush the remaining string content before returning op codes. + flushTo(); - return visitor.opCodes; + return opCodes; } From 14dd90f764a4166ca0315fc77940ca72ce822625 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Thu, 24 Feb 2022 16:13:54 -0800 Subject: [PATCH 26/29] Change files --- ...-fast-element-3ee397c7-e268-4a7d-b842-0f5cf799d3db.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@microsoft-fast-element-3ee397c7-e268-4a7d-b842-0f5cf799d3db.json diff --git a/change/@microsoft-fast-element-3ee397c7-e268-4a7d-b842-0f5cf799d3db.json b/change/@microsoft-fast-element-3ee397c7-e268-4a7d-b842-0f5cf799d3db.json new file mode 100644 index 00000000000..e9ed860aaf1 --- /dev/null +++ b/change/@microsoft-fast-element-3ee397c7-e268-4a7d-b842-0f5cf799d3db.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "automated update of api-report.md", + "packageName": "@microsoft/fast-element", + "email": "nicholasrice@users.noreply.github.com", + "dependentChangeType": "none" +} From cdc612fe83d5bb458301eedf967fe2edd1072015 Mon Sep 17 00:00:00 2001 From: Nicholas Rice <3213292+nicholasrice@users.noreply.github.com> Date: Fri, 25 Feb 2022 12:25:16 -0800 Subject: [PATCH 27/29] Update packages/web-components/fast-ssr/src/template-parser/template-parser.ts Co-authored-by: Jane Chu <7559015+janechu@users.noreply.github.com> --- .../fast-ssr/src/template-parser/template-parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index d7ffd159cc2..0f74a989132 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -147,7 +147,7 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { if (typeof html !== "string") { throw new Error( - "@microsoft/fast-ssr does not support rendering a ViewTemplate with an HTMLTemplateElement html source." + "@microsoft/fast-ssr only supports rendering a ViewTemplate with a string source." ); } From f6f44189a489cf0dfc5e9c18097bf9ddc769f815 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Fri, 25 Feb 2022 15:27:17 -0800 Subject: [PATCH 28/29] rename ast variable --- .../fast-ssr/src/template-parser/template-parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index 0f74a989132..b3f667e64e4 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -156,9 +156,9 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { * below, so store in a new var that is just a string type */ const templateString = html; - const ast = parseFragment(html, { sourceCodeLocationInfo: true }); + const nodeTree = parseFragment(html, { sourceCodeLocationInfo: true }); - if (!("nodeName" in ast)) { + if (!("nodeName" in nodeTree)) { // I'm not sure when exactly this is encountered but the type system seems to say it's possible. throw new Error(`Error parsing template:\n${template}`); } @@ -310,7 +310,7 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { lastOffset = offset; } - traverse(ast, { + traverse(nodeTree, { visit(node: DefaultTreeNode): void { if (isCommentMarkerNode(node)) { flushTo(node.sourceCodeLocation.startOffset); From 74e5040e3a41140078e44f2183cfe6ac12592782 Mon Sep 17 00:00:00 2001 From: nicholasrice Date: Tue, 1 Mar 2022 11:41:43 -0800 Subject: [PATCH 29/29] remove dependency on Markup.marker --- .../fast-ssr/src/template-parser/marker.ts | 19 ------ .../src/template-parser/template-parser.ts | 58 +++---------------- 2 files changed, 9 insertions(+), 68 deletions(-) delete mode 100644 packages/web-components/fast-ssr/src/template-parser/marker.ts diff --git a/packages/web-components/fast-ssr/src/template-parser/marker.ts b/packages/web-components/fast-ssr/src/template-parser/marker.ts deleted file mode 100644 index fc78b67e19d..00000000000 --- a/packages/web-components/fast-ssr/src/template-parser/marker.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DefaultTreeCommentNode } from "parse5"; -import { Markup } from "@microsoft/fast-element"; - -const { marker } = Markup; -const blockMarker = new RegExp(`${marker}:\\d+`); -export function isMarkerComment(node: DefaultTreeCommentNode): boolean { - return blockMarker.test(node.data); -} - -const interpolationMarker = new RegExp(`${marker}\\{(?\\d+)\\}${marker}`); -export function isInterpolationMarker(node: { value: string }): boolean { - return interpolationMarker.test(node.value); -} - -export function extractInterpolationMarkerId(node: { value: string }): number | null { - const id = interpolationMarker.exec(node.value)?.groups?.id; - - return id === undefined ? null : parseInt(id, 10); -} diff --git a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts index b3f667e64e4..83db03d85ab 100644 --- a/packages/web-components/fast-ssr/src/template-parser/template-parser.ts +++ b/packages/web-components/fast-ssr/src/template-parser/template-parser.ts @@ -2,12 +2,7 @@ * This code is largely a fork of lit's rendering implementation: https://github.com/lit/lit/blob/main/packages/labs/ssr/src/lib/render-lit-html.ts * with changes as necessary to render FAST components. A big thank you to those who contributed to lit's code above. */ -import { - InlinableHTMLDirective, - Markup, - Parser, - ViewTemplate, -} from "@microsoft/fast-element"; +import { Parser, ViewTemplate } from "@microsoft/fast-element"; import { Attribute, DefaultTreeCommentNode, @@ -18,7 +13,6 @@ import { parseFragment, } from "parse5"; import { AttributeType, attributeTypeRegExp } from "./attributes.js"; -import { isInterpolationMarker, isMarkerComment } from "./marker.js"; import { Op, OpType } from "./op-codes.js"; /** @@ -105,34 +99,6 @@ function getAttributeType(attr: Attribute): AttributeType { : AttributeType.content; } -/** - * Tests if a node is an interpolated FAST marker - * @param node - the node to test - */ -function isInterpolationMarkerNode( - node: DefaultTreeNode -): node is Required { - return ( - isTextNode(node) && - node.sourceCodeLocation !== undefined && - isInterpolationMarker(node) - ); -} - -/** - * Tests if a node is a FAST comment marker - * @param node - the node to test - */ -function isCommentMarkerNode( - node: DefaultTreeNode -): node is Required { - return ( - isCommentNode(node) && - node.sourceCodeLocation !== undefined && - isMarkerComment(node) - ); -} - /** * Parses a template into a set of operation instructions * @param template - The template to parse @@ -197,7 +163,7 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { dynamic: Attribute[]; } = node.attrs.reduce( (prev, current) => { - if (isInterpolationMarker(current)) { + if (Parser.parse(current.value, directives)) { prev.dynamic.push(current); } else { prev.static.set(current.name, current.value); @@ -312,26 +278,20 @@ export function parseTemplateToOpCodes(template: ViewTemplate): Op[] { traverse(nodeTree, { visit(node: DefaultTreeNode): void { - if (isCommentMarkerNode(node)) { - flushTo(node.sourceCodeLocation.startOffset); - const directive = directives[ - Markup.indexFromComment((node as unknown) as Comment) - ] as InlinableHTMLDirective; - - opCodes.push({ type: OpType.directive, directive }); - skipTo(node.sourceCodeLocation.endOffset); - } else if (isInterpolationMarkerNode(node)) { - flushTo(node.sourceCodeLocation.startOffset); - const parsed = Parser.parse(node.value, directives); + if (isCommentNode(node) || isTextNode(node)) { + const value = + (node as DefaultTreeCommentNode).data || + (node as DefaultTreeTextNode).value; + const parsed = Parser.parse(value, directives); if (parsed) { + flushTo(node.sourceCodeLocation!.startOffset); opCodes.push({ type: OpType.directive, directive: Parser.aggregate(parsed), }); + skipTo(node.sourceCodeLocation!.endOffset); } - - skipTo(node.sourceCodeLocation.endOffset); } else if (isElementNode(node)) { parseElementNode(node); }