mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-16 13:56:35 +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),
|
chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
|
||||||
path: latestBuild ? paths.output : paths.output_es5,
|
path: latestBuild ? paths.output : paths.output_es5,
|
||||||
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
|
||||||
|
// For workerize loader
|
||||||
|
globalObject: "self",
|
||||||
},
|
},
|
||||||
resolve,
|
resolve,
|
||||||
};
|
};
|
||||||
@ -210,6 +212,8 @@ const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
|
|||||||
latestBuild ? "frontend_latest" : "frontend_es5"
|
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||||
),
|
),
|
||||||
publicPath: 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"
|
latestBuild ? "frontend_latest" : "frontend_es5"
|
||||||
),
|
),
|
||||||
publicPath: 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-cli": "^3.3.0",
|
||||||
"webpack-dev-server": "^3.2.1",
|
"webpack-dev-server": "^3.2.1",
|
||||||
"webpack-manifest-plugin": "^2.0.4",
|
"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": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
||||||
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
"_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"
|
json5 "^0.5.0"
|
||||||
object-assign "^4.0.1"
|
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"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
|
||||||
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
|
||||||
@ -14362,6 +14362,13 @@ worker-farm@^1.5.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
errno "~0.1.7"
|
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:
|
wrap-ansi@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
|
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user