mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-15 21:36:36 +00:00
Run markdown in web worker (#3524)
* Run markdown in web worker * Set global object
This commit is contained in:
parent
a66960fa00
commit
cdcafe9e6f
@ -170,6 +170,8 @@ const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
|
||||
path: latestBuild ? paths.output : paths.output_es5,
|
||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||
// For workerize loader
|
||||
globalObject: "self",
|
||||
},
|
||||
resolve,
|
||||
};
|
||||
@ -210,6 +212,8 @@ const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
||||
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||
),
|
||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||
// For workerize loader
|
||||
globalObject: "self",
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -255,6 +259,8 @@ const createCastConfig = ({ isProdBuild, latestBuild }) => {
|
||||
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||
),
|
||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||
// For workerize loader
|
||||
globalObject: "self",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -170,7 +170,8 @@
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"webpack-manifest-plugin": "^2.0.4",
|
||||
"workbox-webpack-plugin": "^4.1.1"
|
||||
"workbox-webpack-plugin": "^4.1.1",
|
||||
"workerize-loader": "^1.1.0"
|
||||
},
|
||||
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
||||
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
||||
|
@ -1,96 +0,0 @@
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import { EventsMixin } from "../mixins/events-mixin";
|
||||
|
||||
let loaded = null;
|
||||
|
||||
const tagWhiteList = ["svg", "path", "ha-icon"];
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
*/
|
||||
class HaMarkdown extends EventsMixin(PolymerElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
content: {
|
||||
observer: "_render",
|
||||
type: String,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
// 0 = not loaded, 1 = success, 2 = error
|
||||
this._scriptLoaded = 0;
|
||||
this._renderScheduled = false;
|
||||
this._resize = () => this.fire("iron-resize");
|
||||
|
||||
if (!loaded) {
|
||||
loaded = import(/* webpackChunkName: "load_markdown" */ "../resources/load_markdown");
|
||||
}
|
||||
loaded
|
||||
.then(
|
||||
({ marked, filterXSS }) => {
|
||||
this.marked = marked;
|
||||
this.filterXSS = filterXSS;
|
||||
this._scriptLoaded = 1;
|
||||
},
|
||||
() => {
|
||||
this._scriptLoaded = 2;
|
||||
}
|
||||
)
|
||||
.then(() => this._render());
|
||||
}
|
||||
|
||||
_render() {
|
||||
if (this._scriptLoaded === 0 || this._renderScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._renderScheduled = true;
|
||||
|
||||
// debounce it to next microtask.
|
||||
Promise.resolve().then(() => {
|
||||
this._renderScheduled = false;
|
||||
|
||||
if (this._scriptLoaded === 1) {
|
||||
this.innerHTML = this.filterXSS(
|
||||
this.marked(this.content, {
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
}),
|
||||
{
|
||||
onIgnoreTag: (tag, html) =>
|
||||
tagWhiteList.indexOf(tag) >= 0 ? html : null,
|
||||
}
|
||||
);
|
||||
this._resize();
|
||||
|
||||
const walker = document.createTreeWalker(
|
||||
this,
|
||||
1 /* SHOW_ELEMENT */,
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
while (walker.nextNode()) {
|
||||
const node = walker.currentNode;
|
||||
|
||||
// Open external links in a new window
|
||||
if (node.tagName === "A" && node.host !== document.location.host) {
|
||||
node.target = "_blank";
|
||||
|
||||
// Fire a resize event when images loaded to notify content resized
|
||||
} else if (node.tagName === "IMG") {
|
||||
node.addEventListener("load", this._resize);
|
||||
}
|
||||
}
|
||||
} else if (this._scriptLoaded === 2) {
|
||||
this.innerText = this.content;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-markdown", HaMarkdown);
|
64
src/components/ha-markdown.ts
Normal file
64
src/components/ha-markdown.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { UpdatingElement, property, customElement } from "lit-element";
|
||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
||||
// @ts-ignore
|
||||
// tslint:disable-next-line: no-implicit-dependencies
|
||||
import markdownWorker from "workerize-loader!../resources/markdown_worker";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
|
||||
let worker: any | undefined;
|
||||
|
||||
@customElement("ha-markdown")
|
||||
class HaMarkdown extends UpdatingElement {
|
||||
@property() public content = "";
|
||||
|
||||
protected update(changedProps) {
|
||||
super.update(changedProps);
|
||||
|
||||
if (!worker) {
|
||||
worker = markdownWorker();
|
||||
}
|
||||
|
||||
this._render();
|
||||
}
|
||||
|
||||
private async _render() {
|
||||
this.innerHTML = await worker.renderMarkdown(this.content, {
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
});
|
||||
|
||||
this._resize();
|
||||
|
||||
const walker = document.createTreeWalker(
|
||||
this,
|
||||
1 /* SHOW_ELEMENT */,
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
while (walker.nextNode()) {
|
||||
const node = walker.currentNode;
|
||||
|
||||
// Open external links in a new window
|
||||
if (
|
||||
node.nodeName === "A" &&
|
||||
(node as HTMLAnchorElement).host !== document.location.host
|
||||
) {
|
||||
(node as HTMLAnchorElement).target = "_blank";
|
||||
|
||||
// Fire a resize event when images loaded to notify content resized
|
||||
} else if (node.nodeName === "IMG") {
|
||||
node.addEventListener("load", this._resize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _resize = () => fireEvent(this, "iron-resize");
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-markdown": HaMarkdown;
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import marked_ from "marked";
|
||||
import filterXSS_ from "xss";
|
||||
|
||||
export const marked = marked_;
|
||||
export const filterXSS = filterXSS_;
|
14
src/resources/markdown_worker.ts
Normal file
14
src/resources/markdown_worker.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import marked from "marked";
|
||||
// @ts-ignore
|
||||
import filterXSS from "xss";
|
||||
|
||||
export const renderMarkdown = async (
|
||||
content: string,
|
||||
markedOptions: object
|
||||
) => {
|
||||
return filterXSS(marked(content, markedOptions), {
|
||||
onIgnoreTag(tag, html) {
|
||||
return ["svg", "path", "ha-icon"].indexOf(tag) !== -1 ? html : null;
|
||||
},
|
||||
});
|
||||
};
|
@ -8639,7 +8639,7 @@ loader-utils@^0.2.16:
|
||||
json5 "^0.5.0"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
loader-utils@^1.0.2, loader-utils@^1.1.0:
|
||||
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
||||
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
||||
@ -14362,6 +14362,13 @@ worker-farm@^1.5.2:
|
||||
dependencies:
|
||||
errno "~0.1.7"
|
||||
|
||||
workerize-loader@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/workerize-loader/-/workerize-loader-1.1.0.tgz#d3a634390dcb685cc1ee292cd1fffeef0a646044"
|
||||
integrity sha512-cU2jPVE3AzzVxOonBe9lCCO//qwE9s/K4a9njFVRLueznzNDNND5vGHVorGuzK6xvamdDOZ9+g7CPIc7QKzucQ==
|
||||
dependencies:
|
||||
loader-utils "^1.2.3"
|
||||
|
||||
wrap-ansi@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
|
||||
|
Loading…
x
Reference in New Issue
Block a user