From a66d2ca1b9a2bc34bbe8dca358e5982760bfeede Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 18 May 2020 07:51:46 -0700 Subject: [PATCH] Use comlink in workers (#5915) --- build-scripts/webpack.js | 4 +- package.json | 4 +- src/components/data-table/ha-data-table.ts | 13 +-- src/components/data-table/sort-filter.ts | 26 ++++++ .../data-table/sort_filter_worker.ts | 84 ++++++++----------- src/components/ha-markdown.ts | 13 +-- src/entrypoints/compatibility.ts | 2 + src/resources/markdown_worker.ts | 14 +++- src/resources/render-markdown.ts | 18 ++++ yarn.lock | 20 +++-- 10 files changed, 117 insertions(+), 81 deletions(-) create mode 100644 src/components/data-table/sort-filter.ts create mode 100644 src/resources/render-markdown.ts diff --git a/build-scripts/webpack.js b/build-scripts/webpack.js index 5cbb12bfea..08d419c446 100644 --- a/build-scripts/webpack.js +++ b/build-scripts/webpack.js @@ -2,6 +2,7 @@ const webpack = require("webpack"); const path = require("path"); const TerserPlugin = require("terser-webpack-plugin"); const ManifestPlugin = require("webpack-manifest-plugin"); +const WorkerPlugin = require("worker-plugin"); const paths = require("./paths.js"); const env = require("./env.js"); const { babelLoaderConfig } = require("./babel.js"); @@ -51,6 +52,7 @@ const createWebpackConfig = ({ ], }, plugins: [ + new WorkerPlugin(), new ManifestPlugin(), new webpack.DefinePlugin({ __DEV__: !isProdBuild, @@ -105,7 +107,7 @@ const createWebpackConfig = ({ latestBuild ? "frontend_latest" : "frontend_es5" ), publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/", - // For workerize loader + // To silence warning in worker plugin globalObject: "self", }, }; diff --git a/package.json b/package.json index de6b2e6d4d..193aefcd0f 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "chart.js": "~2.8.0", "chartjs-chart-timeline": "^0.3.0", "codemirror": "^5.49.0", + "comlink": "^4.3.0", "cpx": "^1.5.0", "deep-clone-simple": "^1.1.1", "deep-freeze": "^0.0.1", @@ -108,6 +109,7 @@ "memoize-one": "^5.0.2", "moment": "^2.24.0", "node-vibrant": "^3.1.5", + "proxy-polyfill": "^0.3.1", "regenerator-runtime": "^0.13.2", "resize-observer": "^1.0.0", "roboto-fontface": "^0.10.0", @@ -195,7 +197,7 @@ "webpack-dev-server": "^3.10.3", "webpack-manifest-plugin": "^2.0.4", "workbox-build": "^5.1.3", - "workerize-loader": "^1.1.0" + "worker-plugin": "^4.0.3" }, "_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page", "_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569", diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index be9b5c246c..cf8407ed66 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -14,9 +14,6 @@ import { classMap } from "lit-html/directives/class-map"; import { ifDefined } from "lit-html/directives/if-defined"; import { styleMap } from "lit-html/directives/style-map"; import { scroll } from "lit-virtualizer"; -// @ts-ignore -// eslint-disable-next-line import/no-webpack-loader-syntax -import sortFilterWorker from "workerize-loader!./sort_filter_worker"; import { fireEvent } from "../../common/dom/fire_event"; import "../../common/search/search-input"; import { debounce } from "../../common/util/debounce"; @@ -24,6 +21,7 @@ import { nextRender } from "../../common/util/render-status"; import "../ha-checkbox"; import type { HaCheckbox } from "../ha-checkbox"; import "../ha-icon"; +import { filterSortData } from "./sort-filter"; declare global { // for fire event @@ -117,8 +115,6 @@ export class HaDataTable extends LitElement { private curRequest = 0; - private _worker: any | undefined; - private _debounceSearch = debounce( (value: string) => { this._filter = value; @@ -140,11 +136,6 @@ export class HaDataTable extends LitElement { } } - protected firstUpdated(properties: PropertyValues) { - super.firstUpdated(properties); - this._worker = sortFilterWorker(); - } - protected updated(properties: PropertyValues) { super.updated(properties); @@ -383,7 +374,7 @@ export class HaDataTable extends LitElement { this.curRequest++; const curRequest = this.curRequest; - const filterProm = this._worker.filterSortData( + const filterProm = filterSortData( this.data, this._sortColumns, this._filter, diff --git a/src/components/data-table/sort-filter.ts b/src/components/data-table/sort-filter.ts new file mode 100644 index 0000000000..766a167fba --- /dev/null +++ b/src/components/data-table/sort-filter.ts @@ -0,0 +1,26 @@ +import { wrap } from "comlink"; + +type FilterSortDataType = typeof import("./sort_filter_worker").api["filterSortData"]; +type filterSortDataParamTypes = Parameters; + +let worker: any | undefined; + +export const filterSortData = async ( + data: filterSortDataParamTypes[0], + columns: filterSortDataParamTypes[1], + filter: filterSortDataParamTypes[2], + direction: filterSortDataParamTypes[3], + sortColumn: filterSortDataParamTypes[4] +): Promise> => { + if (!worker) { + worker = wrap(new Worker("./sort_filter_worker", { type: "module" })); + } + + return await worker.filterSortData( + data, + columns, + filter, + direction, + sortColumn + ); +}; diff --git a/src/components/data-table/sort_filter_worker.ts b/src/components/data-table/sort_filter_worker.ts index 1bae239169..f7b71bdd2c 100644 --- a/src/components/data-table/sort_filter_worker.ts +++ b/src/components/data-table/sort_filter_worker.ts @@ -1,57 +1,34 @@ -import memoizeOne from "memoize-one"; -// eslint-disable-next-line import/no-cycle -import { - DataTableColumnContainer, - DataTableColumnData, +// To use comlink under ES5 +import "proxy-polyfill"; +import { expose } from "comlink"; +import type { + DataTableSortColumnData, DataTableRowData, SortingDirection, + HaDataTable, } from "./ha-data-table"; -export const filterSortData = memoizeOne( - async ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - filter: string, - direction: SortingDirection, - sortColumn?: string - ) => - sortColumn - ? _memSortData( - await _memFilterData(data, columns, filter), - columns, - direction, - sortColumn - ) - : _memFilterData(data, columns, filter) -); +type SortableColumnContainer = HaDataTable["_sortColumns"]; -const _memFilterData = memoizeOne( - async ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - filter: string - ) => { - if (!filter) { - return data; - } - return filterData(data, columns, filter.toUpperCase()); - } -); - -const _memSortData = memoizeOne( - ( - data: DataTableRowData[], - columns: DataTableColumnContainer, - direction: SortingDirection, - sortColumn: string - ) => { - return sortData(data, columns[sortColumn], direction, sortColumn); - } -); - -export const filterData = ( +const filterSortData = ( data: DataTableRowData[], - columns: DataTableColumnContainer, + columns: SortableColumnContainer, + filter: string, + direction: SortingDirection, + sortColumn?: string +) => { + const filteredData = filter ? filterData(data, columns, filter) : data; + + if (!sortColumn) { + return filteredData; + } + + return sortData(filteredData, columns, direction, sortColumn); +}; + +const filterData = ( + data: DataTableRowData[], + columns: SortableColumnContainer, filter: string ) => data.filter((row) => { @@ -70,9 +47,9 @@ export const filterData = ( }); }); -export const sortData = ( +const sortData = ( data: DataTableRowData[], - column: DataTableColumnData, + column: DataTableSortColumnData, direction: SortingDirection, sortColumn: string ) => @@ -105,3 +82,10 @@ export const sortData = ( } return 0; }); + +// Export for types +export const api = { + filterSortData, +}; + +expose(api); diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts index ee96a2b219..b5ba2e6697 100644 --- a/src/components/ha-markdown.ts +++ b/src/components/ha-markdown.ts @@ -1,10 +1,6 @@ import { customElement, property, UpdatingElement } from "lit-element"; -// @ts-ignore -// eslint-disable-next-line import/no-webpack-loader-syntax -import markdownWorker from "workerize-loader!../resources/markdown_worker"; import { fireEvent } from "../common/dom/fire_event"; - -let worker: any | undefined; +import { renderMarkdown } from "../resources/render-markdown"; @customElement("ha-markdown") class HaMarkdown extends UpdatingElement { @@ -16,16 +12,11 @@ class HaMarkdown extends UpdatingElement { protected update(changedProps) { super.update(changedProps); - - if (!worker) { - worker = markdownWorker(); - } - this._render(); } private async _render() { - this.innerHTML = await worker.renderMarkdown( + this.innerHTML = await renderMarkdown( this.content, { breaks: this.breaks, diff --git a/src/entrypoints/compatibility.ts b/src/entrypoints/compatibility.ts index e97a4ebc4a..9b54cfeef2 100644 --- a/src/entrypoints/compatibility.ts +++ b/src/entrypoints/compatibility.ts @@ -2,6 +2,8 @@ import objAssign from "es6-object-assign"; import "mdn-polyfills/Array.prototype.includes"; import "regenerator-runtime/runtime"; import "unfetch/polyfill"; +// To use comlink under ES5 +import "proxy-polyfill"; objAssign.polyfill(); diff --git a/src/resources/markdown_worker.ts b/src/resources/markdown_worker.ts index f3bdbc1ee1..70507bb9fa 100644 --- a/src/resources/markdown_worker.ts +++ b/src/resources/markdown_worker.ts @@ -1,3 +1,6 @@ +// To use comlink under ES5 +import "proxy-polyfill"; +import { expose } from "comlink"; import marked from "marked"; // @ts-ignore import filterXSS from "xss"; @@ -9,14 +12,14 @@ interface WhiteList { let whiteListNormal: WhiteList | undefined; let whiteListSvg: WhiteList | undefined; -export const renderMarkdown = ( +const renderMarkdown = ( content: string, markedOptions: object, hassOptions: { // Do not allow SVG on untrusted content, it allows XSS. allowSvg?: boolean; } = {} -) => { +): string => { if (!whiteListNormal) { whiteListNormal = { ...filterXSS.whiteList, @@ -45,3 +48,10 @@ export const renderMarkdown = ( whiteList, }); }; + +// Export for types +export const api = { + renderMarkdown, +}; + +expose(api); diff --git a/src/resources/render-markdown.ts b/src/resources/render-markdown.ts new file mode 100644 index 0000000000..d4a4397f21 --- /dev/null +++ b/src/resources/render-markdown.ts @@ -0,0 +1,18 @@ +import { wrap } from "comlink"; + +type RenderMarkdownType = typeof import("./markdown_worker").api["renderMarkdown"]; +type renderMarkdownParamTypes = Parameters; + +let worker: any | undefined; + +export const renderMarkdown = async ( + content: renderMarkdownParamTypes[0], + markedOptions: renderMarkdownParamTypes[1], + hassOptions?: renderMarkdownParamTypes[2] +): Promise> => { + if (!worker) { + worker = wrap(new Worker("./markdown_worker", { type: "module" })); + } + + return await worker.renderMarkdown(content, markedOptions, hassOptions); +}; diff --git a/yarn.lock b/yarn.lock index 2f05c6bbd9..0b41c7c7d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5691,6 +5691,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +comlink@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/comlink/-/comlink-4.3.0.tgz#80b3366baccd87897dab3638ebfcfae28b2f87c7" + integrity sha512-mu4KKKNuW8TvkfpW/H88HBPeILubBS6T94BdD1VWBXNXfiyqVtwUCVNO1GeNOBTsIswzsMjWlycYr+77F5b84g== + command-line-args@^5.0.2: version "5.1.0" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.1.0.tgz#632d3d3df35c8f0cc4365e442a3fd6d63b65621b" @@ -12146,6 +12151,11 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.0" +proxy-polyfill@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/proxy-polyfill/-/proxy-polyfill-0.3.1.tgz#163d5283cf928dd8ddb5c5e88528e4ccd233496f" + integrity sha512-jywE1NIksgIGqZc4uF0QLbXGz2RcHQobsCkAW+8F0nr/6agap+TWksEAKyLnIBafPD88HT9qZR2ec0oomHdjcQ== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -15517,12 +15527,12 @@ worker-farm@^1.7.0: 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== +worker-plugin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-4.0.3.tgz#7c42e600d5931ad154d3d5f187a32446df64db0f" + integrity sha512-7hFDYWiKcE3yHZvemsoM9lZis/PzurHAEX1ej8PLCu818Rt6QqUAiDdxHPCKZctzmhqzPpcFSgvMCiPbtooqAg== dependencies: - loader-utils "^1.2.3" + loader-utils "^1.1.0" wrap-ansi@^2.0.0: version "2.1.0"